/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jena.reasoner.rulesys.test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.*; import java.io.*; import org.apache.jena.graph.* ; import org.apache.jena.rdf.model.* ; import org.apache.jena.reasoner.* ; import org.apache.jena.reasoner.rulesys.* ; import org.apache.jena.reasoner.rulesys.impl.* ; import org.apache.jena.reasoner.test.TestUtil ; import org.apache.jena.util.PrintUtil ; import org.apache.jena.vocabulary.* ; /** * Unit tests for simple infrastructure pieces of the rule systems. */ public class TestBasics extends TestCase { // Maximum size of binding environment needed in the tests private static final int MAX_VARS = 10; // Useful constants Node p = NodeFactory.createURI("p"); Node q = NodeFactory.createURI("q"); Node r = NodeFactory.createURI("r"); Node s = NodeFactory.createURI("s"); Node n1 = NodeFactory.createURI("n1"); Node n2 = NodeFactory.createURI("n2"); Node n3 = NodeFactory.createURI("n3"); Node n4 = NodeFactory.createURI("n4"); Node n5 = NodeFactory.createURI("n5"); Node res = NodeFactory.createURI("res"); /** * Boilerplate for junit */ public TestBasics( String name ) { super( name ); } /** * Boilerplate for junit. * This is its own test suite */ public static TestSuite suite() { return new TestSuite( TestBasics.class ); } /** * Test the internal rule parser */ public void testRuleParser() { String[] testRules = new String[] { "(?a rdf:type ?_) -> (?a rdf:type ?b).", "(?a rdf:type ?_), (?a rdf:type ?_) -> (?a rdf:type ?b).", "(?a rdf:type max(?a,1)) -> (?a rdf:type 'foo').", "(?a rdf:type ?_) -> addOne(?a).", "(?a rdf:type ?_) -> [(?a rdf:type ?_) -> addOne(?a)].", "(?a rdf:type ?_) -> (?a rdf:type '42').", "(?a rdf:type ?_) -> (?a rdf:type 4.2).", "(?a rdf:type ?_) -> (?a rdf:type ' fool that,I(am)').", "[rule1: (?a rdf:type ?_) -> (?a rdf:type a)]", "-> print(' ').", "-> print(' literal with embedded \\' characters ').", "-> print(\" literal characters \").", "-> print(42). ", "-> print('42'^^xsd:byte). ", "-> print('42'^^http://www.w3.org/2001/XMLSchema#int). ", "-> print('42'^^foobar:byte). ", "-> print(<foo://a/file>). " }; String[] testResults = new String[] { "[ (?a rdf:type ?_) -> (?a rdf:type ?b) ]", "[ (?a rdf:type ?_) (?a rdf:type ?_) -> (?a rdf:type ?b) ]", "[ (?a rdf:type 'max(?a '1'^^http://www.w3.org/2001/XMLSchema#int)'^^urn:x-hp-jena:Functor) -> (?a rdf:type 'foo') ]", "[ (?a rdf:type ?_) -> addOne(?a) ]", "[ (?a rdf:type ?_) -> [ (?a rdf:type ?_) -> addOne(?a) ] ]", "[ (?a rdf:type ?_) -> (?a rdf:type '42') ]", "[ (?a rdf:type ?_) -> (?a rdf:type '4.2'^^http://www.w3.org/2001/XMLSchema#float) ]", "[ (?a rdf:type ?_) -> (?a rdf:type ' fool that,I(am)') ]", "[ rule1: (?a rdf:type ?_) -> (?a rdf:type <a>) ]", "[ -> print(' ') ]", "[ -> print(' literal with embedded ' characters ') ]", "[ -> print(' literal characters ') ]", "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#int) ]", "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#byte) ]", "[ -> print('42'^^http://www.w3.org/2001/XMLSchema#int) ]", "[ -> print('42'^^http://foobar#byte) ]", "[ -> print(<foo://a/file>) ]", }; PrintUtil.registerPrefix("foobar", "http://foobar#"); for (int i = 0; i < testRules.length; i++) { Rule r = Rule.parseRule(testRules[i]); assertEquals(testResults[i], r.toString()); } // Test for an illegal rule format String[] testBadRules = new String[] { "(foo(?A) eg:p ?B) <- (?a, eg:p, ?B)." , "(foo(?A) eg:p ?B) -> (?a, eg:p, ?B)." }; for ( String testBadRule : testBadRules ) { boolean foundError = false; try { Rule r = Rule.parseRule( testBadRule ); } catch ( Rule.ParserException e ) { foundError = true; } assertTrue( "Failed to find illegal rule", foundError ); } } /** * Test rule equality operations. */ public void testRuleEquality() { Rule r1 = Rule.parseRule("(?a p ?b) -> (?a q ?b)."); Rule r2 = Rule.parseRule("(?a p ?b) -> (?b q ?a)."); Rule r1b = Rule.parseRule("(?x p ?y) -> (?x q ?y)."); Rule r3 = Rule.parseRule("(?a p ?b), addOne(?a) -> (?a q ?b)."); Rule r3b = Rule.parseRule("(?c p ?d), addOne(?c) -> (?c q ?d)."); Rule r4 = Rule.parseRule("(?a p ?b), makeTemp(?a) -> (?a q ?b)."); Rule r5 = Rule.parseRule("(?a p ?b), addOne(?b) -> (?a q ?b)."); Rule r6 = Rule.parseRule("(?a p ?b), addOne(p) -> (?a q ?b)."); assertTrue(! r1.equals(r2)); assertTrue( r1.equals(r1b)); assertTrue(! r1.equals(r3)); assertTrue( r3.equals(r3b)); assertTrue(! r3.equals(r4)); assertTrue(! r3.equals(r5)); assertTrue(! r3.equals(r6)); } /** * Test the BindingEnvironment machinery */ public void testBindingEnvironment() { BindingStack env = new BindingStack(); env.reset(MAX_VARS); env.bind(3, n1); assertEquals(n1, env.getEnvironment()[3]); env.push(); env.bind(2, n2); assertEquals(n2, env.getEnvironment()[2]); env.unwind(); assertEquals(null, env.getEnvironment()[2]); assertEquals(n1, env.getEnvironment()[3]); env.push(); env.bind(1, n3); assertEquals(null, env.getEnvironment()[2]); assertEquals(n1, env.getEnvironment()[3]); assertEquals(n3, env.getEnvironment()[1]); env.unwind(); assertEquals(null, env.getEnvironment()[2]); assertEquals(n1, env.getEnvironment()[3]); assertEquals(null, env.getEnvironment()[1]); env.push(); env.bind(1, n3); assertEquals(null, env.getEnvironment()[2]); assertEquals(n1, env.getEnvironment()[3]); assertEquals(n3, env.getEnvironment()[1]); env.commit(); assertEquals(null, env.getEnvironment()[2]); assertEquals(n1, env.getEnvironment()[3]); assertEquals(n3, env.getEnvironment()[1]); try { env.unwind(); assertTrue("Failed to catch end of stack", false); } catch (IndexOutOfBoundsException e) { } } /** * Test simple single clause binding */ public void testClauseMaching() { BindingStack env = new BindingStack(); env.reset(MAX_VARS); List<Rule> rules = new ArrayList<>(); BasicForwardRuleInfGraph inf = new BasicForwardRuleInfGraph( new BasicForwardRuleReasoner(rules), rules, null); TriplePattern p1 = new TriplePattern( new Node_RuleVariable("?a", 0), n1, new Node_RuleVariable("?b", 1)); TriplePattern p2 = new TriplePattern( new Node_RuleVariable("?b", 1), new Node_RuleVariable("?c", 2), n2); // Should fail with no bindings boolean match = FRuleEngine.match(p1, new Triple(n1, n2, n3), env); assertTrue(!match); assertEquals(null, env.getEnvironment()[0]); assertEquals(null, env.getEnvironment()[1]); assertEquals(null, env.getEnvironment()[2]); // Should succeed with two bindings match = FRuleEngine.match(p1, new Triple(n2, n1, n3), env); assertTrue(match); assertEquals(n2, env.getEnvironment()[0]); assertEquals(n3, env.getEnvironment()[1]); assertEquals(null, env.getEnvironment()[2]); // should fail but leave prior bindings intact match = FRuleEngine.match(p2, new Triple(n1, n2, n2), env); assertTrue(!match); assertEquals(n2, env.getEnvironment()[0]); assertEquals(n3, env.getEnvironment()[1]); assertEquals(null, env.getEnvironment()[2]); // should succeed with full binding set match = FRuleEngine.match(p2, new Triple(n3, n1, n2), env); assertTrue(match); assertEquals(n2, env.getEnvironment()[0]); assertEquals(n3, env.getEnvironment()[1]); assertEquals(n1, env.getEnvironment()[2]); } /** * Minimal rule tester to check basic pattern match */ public void testRuleMatcher() { String rules = "[r1: (?a p ?b), (?b q ?c) -> (?a, q, ?c)]" + "[r2: (?a p ?b), (?b p ?c) -> (?a, p, ?c)]" + "[r3: (?a p ?a), (n1 p ?c), (n1, p, ?a) -> (?a, p, ?c)]" + "[r4: (n4 ?p ?a) -> (n4, ?a, ?p)]"; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem()); infgraph.add(new Triple(n1, p, n2)); infgraph.add(new Triple(n2, p, n3)); infgraph.add(new Triple(n2, q, n3)); infgraph.add(new Triple(n4, p, n4)); TestUtil.assertIteratorValues(this, infgraph.find(null, null, null), new Triple[] { new Triple(n1, p, n2), new Triple(n2, p, n3), new Triple(n2, q, n3), new Triple(n4, p, n4), new Triple(n1, p, n3), new Triple(n1, q, n3), new Triple(n4, n4, p), }); } /** * Test derivation machinery */ public void testRuleDerivations() { String rules = "[testRule1: (n1 p ?a) -> (n2, p, ?a)]" + "[testRule2: (n1 q ?a) -> (n2, q, ?a)]" + "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]"; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem()); infgraph.setDerivationLogging(true); infgraph.add(new Triple(n1, p, n3)); infgraph.add(new Triple(n1, q, n4)); infgraph.add(new Triple(n1, q, n3)); TestUtil.assertIteratorValues(this, infgraph.find(null, null, null), new Triple[] { new Triple(n1, p, n3), new Triple(n2, p, n3), new Triple(n1, q, n4), new Triple(n2, q, n4), new Triple(n1, q, n3), new Triple(n2, q, n3), new Triple(res, p, n3) }); Iterator<Derivation> derivs = infgraph.getDerivation(new Triple(res, p, n3)); StringWriter outString = new StringWriter(250); PrintWriter out = new PrintWriter(outString); while (derivs.hasNext()) { Derivation d = derivs.next(); d.printTrace(out, true); } out.flush(); String testString = TestUtil.normalizeWhiteSpace("Rule testRule3 concluded (<res> <p> <n3>) <-\n" + " Rule testRule1 concluded (<n2> <p> <n3>) <-\n" + " Fact (<n1> <p> <n3>)\r\n" + " Rule testRule2 concluded (<n2> <q> <n3>) <-\n" + " Fact (<n1> <q> <n3>)\r\n"); assertEquals(testString, TestUtil.normalizeWhiteSpace(outString.getBuffer().toString())); } /** * Test axiom handling machinery */ public void testAxiomHandling() { String rules = "[testRule1: (n1 p ?a) -> (n2, p, ?a)]" + "[testRule2: (n1 q ?a) -> (n2, q, ?a)]" + "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]" + "[axiom1: -> (n1 p n3)]"; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem()); TestUtil.assertIteratorValues(this, infgraph.find(null, null, null), new Triple[] { new Triple(n1, p, n3), new Triple(n2, p, n3), }); infgraph.add(new Triple(n1, q, n4)); infgraph.add(new Triple(n1, q, n3)); TestUtil.assertIteratorValues(this, infgraph.find(null, null, null), new Triple[] { new Triple(n1, p, n3), new Triple(n2, p, n3), new Triple(n1, q, n4), new Triple(n2, q, n4), new Triple(n1, q, n3), new Triple(n2, q, n3), new Triple(res, p, n3) }); } /** * Test schema partial binding machinery */ public void testSchemaBinding() { String rules = "[testRule1: (n1 p ?a) -> (n2, p, ?a)]" + "[testRule2: (n1 q ?a) -> (n2, q, ?a)]" + "[testRule3: (n2 p ?a), (n2 q ?a) -> (res p ?a)]"; List<Rule> ruleList = Rule.parseRules(rules); Graph schema = Factory.createGraphMem(); schema.add(new Triple(n1, p, n3)); Graph data = Factory.createGraphMem(); data.add(new Triple(n1, q, n4)); data.add(new Triple(n1, q, n3)); Reasoner reasoner = new BasicForwardRuleReasoner(ruleList); Reasoner boundReasoner = reasoner.bindSchema(schema); InfGraph infgraph = boundReasoner.bind(data); TestUtil.assertIteratorValues(this, infgraph.find(null, null, null), new Triple[] { new Triple(n1, p, n3), new Triple(n2, p, n3), new Triple(n1, q, n4), new Triple(n2, q, n4), new Triple(n1, q, n3), new Triple(n2, q, n3), new Triple(res, p, n3) }); } /** * Test functor handling */ public void testEmbeddedFunctors() { String rules = "(?C owl:onProperty ?P), (?C owl:allValuesFrom ?D) -> (?C rb:restriction all(?P, ?D))." + "(?C rb:restriction all(eg:p, eg:D)) -> (?C rb:restriction 'allOK')." + "[ -> (eg:foo eg:prop functor(eg:bar, 1)) ]" + "[ (?x eg:prop functor(eg:bar, ?v)) -> (?x eg:propbar ?v) ]" + "[ (?x eg:prop functor(?v, ?*)) -> (?x eg:propfunc ?v) ]" + ""; List<Rule> ruleList = Rule.parseRules(rules); Model data = ModelFactory.createDefaultModel(); Resource R1 = data.createResource(PrintUtil.egNS + "R1"); Resource D = data.createResource(PrintUtil.egNS + "D"); Property p = data.createProperty(PrintUtil.egNS, "p"); Property prop = data.createProperty(PrintUtil.egNS, "prop"); Property propbar = data.createProperty(PrintUtil.egNS, "propbar"); Property propfunc = data.createProperty(PrintUtil.egNS, "propfunc"); Property rbr = data.createProperty(ReasonerVocabulary.RBNamespace, "restriction"); R1.addProperty(OWL.onProperty, p).addProperty(OWL.allValuesFrom, D); Reasoner reasoner = new BasicForwardRuleReasoner(ruleList); InfGraph infgraph = reasoner.bind(data.getGraph()); Model infModel = ModelFactory.createModelForGraph(infgraph); Resource foo = infModel.createResource(PrintUtil.egNS + "foo"); Resource bar = infModel.createResource(PrintUtil.egNS + "bar"); RDFNode flit = infModel.getResource(R1.getURI()).getRequiredProperty(rbr).getObject(); assertNotNull(flit); assertEquals(flit.toString(), "allOK"); // assertTrue(flit instanceof Literal); // Functor func = (Functor)((Literal)flit).getValue(); // assertEquals("all", func.getName()); // assertEquals(p.getNode(), func.getArgs()[0]); // assertEquals(D.getNode(), func.getArgs()[1]); Literal one = (Literal)foo.getRequiredProperty(propbar).getObject(); assertEquals(new Integer(1), one.getValue()); } /** * The the minimal machinery for supporting builtins */ public void testBuiltins() { String rules = //"[testRule1: (n1 ?p ?a) -> print('rule1test', ?p, ?a)]" + "[r1: (n1 p ?x), addOne(?x, ?y) -> (n1 q ?y)]" + "[r2: (n1 p ?x), lessThan(?x, 3) -> (n2 q ?x)]" + "[axiom1: -> (n1 p 1)]" + "[axiom2: -> (n1 p 4)]" + ""; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem()); TestUtil.assertIteratorValues(this, infgraph.find(n1, q, null), new Triple[] { new Triple(n1, q, Util.makeIntNode(2)), new Triple(n1, q, Util.makeIntNode(5)) }); TestUtil.assertIteratorValues(this, infgraph.find(n2, q, null), new Triple[] { new Triple(n2, q, Util.makeIntNode(1)) }); } /** * The the "remove" builtin */ public void testRemoveBuiltin() { String rules = "[rule1: (?x p ?y), (?x q ?y) -> remove(0)]" + ""; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem()); infgraph.add(new Triple(n1, p, Util.makeIntNode(1))); infgraph.add(new Triple(n1, p, Util.makeIntNode(2))); infgraph.add(new Triple(n1, q, Util.makeIntNode(2))); TestUtil.assertIteratorValues(this, infgraph.find(n1, null, null), new Triple[] { new Triple(n1, p, Util.makeIntNode(1)), new Triple(n1, q, Util.makeIntNode(2)) }); } /** * The the "drop" builtin */ public void testDropBuiltin() { String rules = "[rule1: (?x p ?y) -> drop(0)]" + ""; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(Factory.createGraphMem()); infgraph.add(new Triple(n1, p, Util.makeIntNode(1))); infgraph.add(new Triple(n1, p, Util.makeIntNode(2))); infgraph.add(new Triple(n1, q, Util.makeIntNode(2))); TestUtil.assertIteratorValues(this, infgraph.find(n1, null, null), new Triple[] { new Triple(n1, q, Util.makeIntNode(2)) }); } /** * Test the rebind operation. */ public void testRebind() { String rules = "[rule1: (?x p ?y) -> (?x q ?y)]"; List<Rule> ruleList = Rule.parseRules(rules); Graph data = Factory.createGraphMem(); data.add(new Triple(n1, p, n2)); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(data); TestUtil.assertIteratorValues(this, infgraph.find(n1, null, null), new Triple[] { new Triple(n1, p, n2), new Triple(n1, q, n2) }); Graph ndata = Factory.createGraphMem(); ndata.add(new Triple(n1, p, n3)); infgraph.rebind(ndata); TestUtil.assertIteratorValues(this, infgraph.find(n1, null, null), new Triple[] { new Triple(n1, p, n3), new Triple(n1, q, n3) }); } /** * Test size bug, used to blow up if size was called before any queries. */ public void testSize() { String rules = "[rule1: (?x p ?y) -> (?x q ?y)]"; List<Rule> ruleList = Rule.parseRules(rules); Graph data = Factory.createGraphMem(); data.add(new Triple(n1, p, n2)); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(data); assertEquals(infgraph.size(), 2); } /** * Check validity report implementation, there had been a stupid bug here. */ public void testValidityReport() { StandardValidityReport report = new StandardValidityReport(); report.add(false, "dummy", "dummy1"); report.add(false, "dummy", "dummy3"); assertTrue(report.isValid()); report.add(true, "dummy", "dummy2"); assertTrue( ! report.isValid()); report = new StandardValidityReport(); report.add(false, "dummy", "dummy1"); report.add(true, "dummy", "dummy2"); report.add(false, "dummy", "dummy3"); assertTrue( ! report.isValid()); report = new StandardValidityReport(); report.add(new ValidityReport.Report(false, "dummy", "dummy1")); report.add(new ValidityReport.Report(true, "dummy", "dummy2")); report.add(new ValidityReport.Report(false, "dummy", "dummy3")); assertTrue( ! report.isValid()); } /** * Test the list conversion utility that is used in some of the builtins. */ public void testConvertList() { Graph data = Factory.createGraphMem(); Node first = RDF.Nodes.first; Node rest = RDF.Nodes.rest; Node nil = RDF.Nodes.nil; data.add(new Triple(n1, first, p)); data.add(new Triple(n1, rest, n2)); data.add(new Triple(n2, first, q)); data.add(new Triple(n2, rest, nil)); data.add(new Triple(n3, first, p)); data.add(new Triple(n3, rest, n4)); data.add(new Triple(n4, rest, n5)); data.add(new Triple(n5, first, q)); data.add(new Triple(n5, rest, nil)); String rules = "[rule1: (?x p ?y) -> (?x q ?y)]"; List<Rule> ruleList = Rule.parseRules(rules); InfGraph infgraph = new BasicForwardRuleReasoner(ruleList).bind(data); RuleContext context = new BFRuleContext( (ForwardRuleInfGraphI) infgraph); List<Node> result = Util.convertList(n1, context); assertEquals(result.size(), 2); assertEquals(result.get(0), p); assertEquals(result.get(1), q); List<Node> result2 = Util.convertList(n3, context); assertEquals(result2.size(), 1); assertEquals(result2.get(0), p); } }