/*
* 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 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.GenericRuleReasoner.RuleMode ;
import org.apache.jena.reasoner.test.TestUtil ;
import org.apache.jena.util.FileManager ;
import org.apache.jena.util.LocationMapper ;
import org.apache.jena.util.PrintUtil ;
import org.apache.jena.vocabulary.RDF ;
import org.apache.jena.vocabulary.RDFS ;
import org.apache.jena.vocabulary.ReasonerVocabulary ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Test the packaging of all the reasoners into the GenericRuleReasoner.
* The other tests check out this engine. These tests just need to touch
* enough to validate the packaging.
*/
public class TestGenericRules extends TestCase {
protected static Logger logger = LoggerFactory.getLogger(TestFBRules.class);
// Useful constants
Node p = NodeFactory.createURI("p");
Node q = NodeFactory.createURI("q");
Node r = NodeFactory.createURI("r");
Node s = NodeFactory.createURI("s");
Node t = NodeFactory.createURI("t");
Node a = NodeFactory.createURI("a");
Node b = NodeFactory.createURI("b");
Node c = NodeFactory.createURI("c");
Node d = NodeFactory.createURI("d");
Node C1 = NodeFactory.createURI("C1");
Node C2 = NodeFactory.createURI("C2");
Node C3 = NodeFactory.createURI("C3");
Node ty = RDF.Nodes.type;
Node sC = RDFS.Nodes.subClassOf;
List<Rule> ruleList = Rule.parseRules("[r1: (?a p ?b), (?b p ?c) -> (?a p ?c)]" +
"[r2: (?a q ?b) -> (?a p ?c)]" +
"-> table(p). -> table(q).");
Triple[] ans = new Triple[] { new Triple(a, p, b),
new Triple(b, p, c),
new Triple(a, p, c) };
/**
* Boilerplate for junit
*/
public TestGenericRules( String name ) {
super( name );
}
/**
* Boilerplate for junit.
* This is its own test suite
*/
public static TestSuite suite() {
return new TestSuite( TestGenericRules.class );
// TestSuite suite = new TestSuite();
// suite.addTest(new TestGenericRules( "testFunctorLooping" ));
// return suite;
}
/**
* Minimal rule tester to check basic pattern match, forward style.
*/
public void testForward() {
Graph test = Factory.createGraphMem();
test.add(new Triple(a, p, b));
test.add(new Triple(b, p, c));
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(ruleList);
reasoner.setMode(GenericRuleReasoner.FORWARD);
// Check data bind version
InfGraph infgraph = reasoner.bind(test);
TestUtil.assertIteratorValues(this, infgraph.find(null, p, null), ans);
// Check schema bind version
infgraph = reasoner.bindSchema(test).bind(Factory.createGraphMem());
TestUtil.assertIteratorValues(this, infgraph.find(null, p, null), ans);
}
/**
* Minimal rule tester to check basic pattern match, backward style.
*/
public void testBackward() {
Graph test = Factory.createGraphMem();
test.add(new Triple(a, p, b));
test.add(new Triple(b, p, c));
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(ruleList);
reasoner.setMode(GenericRuleReasoner.BACKWARD);
// Check data bind version
InfGraph infgraph = reasoner.bind(test);
TestUtil.assertIteratorValues(this, infgraph.find(null, p, null), ans);
// Check schema bind version
infgraph = reasoner.bindSchema(test).bind(Factory.createGraphMem());
TestUtil.assertIteratorValues(this, infgraph.find(null, p, null), ans);
}
/**
* Test example hybrid rule.
*/
public void testHybrid() {
Graph data = Factory.createGraphMem();
data.add(new Triple(a, r, b));
data.add(new Triple(p, ty, s));
List<Rule> rules = Rule.parseRules(
"[a1: -> (a rdf:type t)]" +
"[r0: (?x r ?y) -> (?x p ?y)]" +
"[r1: (?p rdf:type s) -> [r1b: (?x ?p ?y) <- (?y ?p ?x)]]" +
"[r2: (?p rdf:type s) -> [r2b: (?x ?p ?x) <- (?x rdf:type t)]]" +
"-> tableAll()."
);
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
reasoner.setMode(GenericRuleReasoner.HYBRID);
InfGraph infgraph = reasoner.bind(data);
infgraph.setDerivationLogging(true);
TestUtil.assertIteratorValues(this,
infgraph.find(null, p, null), new Object[] {
new Triple(a, p, a),
new Triple(a, p, b),
new Triple(b, p, a)
} );
// Check derivation tracing as well
Iterator<Derivation> di = infgraph.getDerivation(new Triple(b, p, a));
assertTrue(di.hasNext());
RuleDerivation d = (RuleDerivation)di.next();
// java.io.PrintWriter out = new java.io.PrintWriter(System.out);
// d.printTrace(out, true);
// out.close();
assertTrue(d.getRule().getName().equals("r1b"));
TestUtil.assertIteratorValues(this, d.getMatches().iterator(), new Object[] { new Triple(a, p, b) });
assertTrue(! di.hasNext());
}
/**
* Test early detection of illegal backward rules.
*/
public void testBRuleErrorHandling() {
Graph data = Factory.createGraphMem();
List<Rule> rules = Rule.parseRules(
"[a1: -> [(?x eg:p ?y) (?x eg:q ?y) <- (?x eg:r ?y)]]"
);
boolean foundException = false;
try {
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
reasoner.setMode(GenericRuleReasoner.HYBRID);
InfGraph infgraph = reasoner.bind(data);
infgraph.prepare();
} catch (ReasonerException e) {
foundException = true;
}
assertTrue("Catching use of multi-headed brules", foundException);
}
/**
* Test example parameter setting
*/
public void testParameters() {
Graph data = Factory.createGraphMem();
data.add(new Triple(a, r, b));
data.add(new Triple(p, ty, s));
Model m = ModelFactory.createDefaultModel();
Resource configuration= m.createResource(GenericRuleReasonerFactory.URI);
configuration.addProperty(ReasonerVocabulary.PROPderivationLogging, "true");
configuration.addProperty(ReasonerVocabulary.PROPruleMode, "hybrid");
configuration.addProperty(ReasonerVocabulary.PROPruleSet, "testing/reasoners/genericRuleTest.rules");
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(configuration);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(null, p, null), new Object[] {
new Triple(a, p, a),
new Triple(a, p, b),
new Triple(b, p, a)
} );
// Check derivation tracing as well
Iterator<Derivation> di = infgraph.getDerivation(new Triple(b, p, a));
assertTrue(di.hasNext());
RuleDerivation d = (RuleDerivation)di.next();
assertTrue(d.getRule().getName().equals("r1b"));
TestUtil.assertIteratorValues(this, d.getMatches().iterator(), new Object[] { new Triple(a, p, b) });
assertTrue(! di.hasNext());
// Check retrieval of configuration
Model m2 = ModelFactory.createDefaultModel();
Resource newConfig = m2.createResource();
reasoner.addDescription(m2, newConfig);
TestUtil.assertIteratorValues(this, newConfig.listProperties(), new Statement[] {
m2.createStatement(newConfig, ReasonerVocabulary.PROPderivationLogging, "true"),
m2.createStatement(newConfig, ReasonerVocabulary.PROPruleMode, "hybrid"),
m2.createStatement(newConfig, ReasonerVocabulary.PROPruleSet, "testing/reasoners/genericRuleTest.rules")
} );
// Manual reconfig and check retrieval of changes
reasoner.setParameter(ReasonerVocabulary.PROPderivationLogging, "false");
newConfig = m2.createResource();
reasoner.addDescription(m2, newConfig);
TestUtil.assertIteratorValues(this, newConfig.listProperties(), new Statement[] {
m2.createStatement(newConfig, ReasonerVocabulary.PROPderivationLogging, "false"),
m2.createStatement(newConfig, ReasonerVocabulary.PROPruleMode, "hybrid"),
m2.createStatement(newConfig, ReasonerVocabulary.PROPruleSet, "testing/reasoners/genericRuleTest.rules")
} );
// Mutiple rule file loading
m = ModelFactory.createDefaultModel();
configuration= m.createResource(GenericRuleReasonerFactory.URI);
configuration.addProperty(ReasonerVocabulary.PROPruleMode, "hybrid");
configuration.addProperty(ReasonerVocabulary.PROPruleSet, "testing/reasoners/ruleset1.rules");
configuration.addProperty(ReasonerVocabulary.PROPruleSet, "testing/reasoners/ruleset2.rules");
reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(configuration);
infgraph = reasoner.bind(Factory.createGraphMem());
Node an = NodeFactory.createURI(PrintUtil.egNS + "a");
Node C = NodeFactory.createURI(PrintUtil.egNS + "C");
Node D = NodeFactory.createURI(PrintUtil.egNS + "D");
TestUtil.assertIteratorValues(this,
infgraph.find(null, null, null), new Object[] {
new Triple(an, RDF.Nodes.type, C),
new Triple(an, RDF.Nodes.type, D),
} );
// Test that the parameter initialization is not be overridden by subclasses
m = ModelFactory.createDefaultModel();
configuration = m.createResource(GenericRuleReasonerFactory.URI);
configuration.addProperty( ReasonerVocabulary.PROPenableTGCCaching, m.createLiteral("true") );
reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(configuration);
InfModel im = ModelFactory.createInfModel(reasoner, ModelFactory.createDefaultModel());
Resource Ac = im.createResource(PrintUtil.egNS + "A");
Resource Bc = im.createResource(PrintUtil.egNS + "B");
Resource Cc = im.createResource(PrintUtil.egNS + "C");
im.add(Ac, RDFS.subClassOf, Bc);
im.add(Bc, RDFS.subClassOf, Cc);
assertTrue("TGC enabled correctly", im.contains(Ac, RDFS.subClassOf, Cc));
}
/**
* Check that the use of typed literals in the configuration also works
*/
public void testTypedConfigParameters() {
Model m = ModelFactory.createDefaultModel();
Resource configuration= m.createResource(GenericRuleReasonerFactory.URI);
configuration.addProperty(ReasonerVocabulary.PROPenableTGCCaching, m.createTypedLiteral(Boolean.TRUE));
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(configuration);
InfModel im = ModelFactory.createInfModel(reasoner, ModelFactory.createDefaultModel());
Resource Ac = im.createResource(PrintUtil.egNS + "A");
Resource Bc = im.createResource(PrintUtil.egNS + "B");
Resource Cc = im.createResource(PrintUtil.egNS + "C");
im.add(Ac, RDFS.subClassOf, Bc);
im.add(Bc, RDFS.subClassOf, Cc);
assertTrue("TGC enabled correctly", im.contains(Ac, RDFS.subClassOf, Cc));
}
/**
* Test control of functor filtering
*/
public void testHybridFunctorFilter() {
Graph data = Factory.createGraphMem();
data.add(new Triple(a, r, b));
data.add(new Triple(a, p, s));
List<Rule> rules = Rule.parseRules( "[r0: (?x r ?y) (?x p ?z) -> (?x q func(?y, ?z)) ]" );
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
reasoner.setMode(GenericRuleReasoner.HYBRID);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(null, q, null), new Object[] {
} );
reasoner.setFunctorFiltering(false);
infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(null, q, null), new Object[] {
new Triple(a, q, Functor.makeFunctorNode("func", new Node[]{b, s}))
} );
}
/**
* Test recursive rules involving functors
* May lock up in there is a bug.
*/
public void testFunctorLooping() {
doTestFunctorLooping(GenericRuleReasoner.FORWARD_RETE);
doTestFunctorLooping(GenericRuleReasoner.HYBRID);
}
/**
* Test recursive rules involving functors.
* May lock up in there is a bug.
* TODO: arrange test to run in a separate thread with a timeout
*/
public void doTestFunctorLooping(RuleMode mode) {
Graph data = Factory.createGraphMem();
data.add(new Triple(a, r, b));
List<Rule> rules = Rule.parseRules( "(?x r ?y) -> (?x p func(?x)). (?x p ?y) -> (?x p func(?x))." );
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
reasoner.setMode(mode);
InfGraph infgraph = reasoner.bind(data);
// The p should have been asserted but is invisible
assertFalse( infgraph.contains(Node.ANY, p, Node.ANY) );
}
/**
* Test the @prefix and @include extensions to the rule parser
*/
public void testExtendedRuleParser() {
List<Rule> rules = Rule.rulesFromURL("file:testing/reasoners/ruleParserTest1.rules");
GenericRuleReasoner reasoner = new GenericRuleReasoner(rules);
reasoner.setTransitiveClosureCaching(true);
Model base = ModelFactory.createDefaultModel();
InfModel m = ModelFactory.createInfModel(reasoner, base);
// Check prefix case
String NS1 = "http://jena.hpl.hp.com/newprefix#";
String NS2 = "http://jena.hpl.hp.com/newprefix2#";
String NS3 = "http://jena.hpl.hp.com/newprefix3#";
Resource A = m.getResource(NS1 + "A");
Resource C = m.getResource(NS1 + "C");
Property p = m.getProperty(NS2 + "p");
Property a = m.getProperty(NS3 + "a");
Resource foo = m.getResource(NS1 + "foo");
assertTrue("@prefix test", m.contains(A, p, foo));
// Check RDFS rule inclusion
assertTrue("@include RDFS test", m.contains(A, RDFS.subClassOf, C));
assertTrue("@include test", m.contains(a,a,a));
}
/**
* Test that @include supports fileManger redirections
*/
public void testIncludeRedirect() {
assertFalse( checkIncludeFound("file:testing/reasoners/importTest.rules") );
LocationMapper lm = FileManager.get().getLocationMapper();
lm.addAltEntry("file:testing/reasoners/includeAlt.rules",
"file:testing/reasoners/include.rules");
assertTrue( checkIncludeFound("file:testing/reasoners/importTest.rules") );
lm.removeAltEntry("file:testing/reasoners/includeAlt.rules");
}
/**
* Check whether the test included file has been found
*/
private boolean checkIncludeFound(String ruleSrc) {
try {
List<Rule> rules = Rule.rulesFromURL(ruleSrc);
GenericRuleReasoner reasoner = new GenericRuleReasoner(rules);
Model base = ModelFactory.createDefaultModel();
InfModel m = ModelFactory.createInfModel(reasoner, base);
// Check prefix case
String NS3 = "http://jena.hpl.hp.com/newprefix3#";
Property a = m.getProperty(NS3 + "a");
return m.contains(a,a,a);
} catch (Exception e) {
return false;
}
}
/**
* Test add/remove support
*/
public void testAddRemove() {
doTestAddRemove(false);
doTestAddRemove(true);
}
/**
* Internals of add/remove test.
* @param useTGC set to true to use transitive caching
*/
public void doTestAddRemove(boolean useTGC) {
Graph data = Factory.createGraphMem();
data.add(new Triple(a, p, C1));
data.add(new Triple(C1, sC, C2));
data.add(new Triple(C2, sC, C3));
List<Rule> rules = Rule.parseRules(
"-> table(rdf:type)." +
"[r1: (?x p ?c) -> (?x rdf:type ?c)] " +
"[rdfs9: (?x rdfs:subClassOf ?y) -> [ (?a rdf:type ?y) <- (?a rdf:type ?x)] ]"
);
if (!useTGC) {
rules.add(Rule.parseRule("[rdfs8: (?a rdfs:subClassOf ?b), (?b rdfs:subClassOf ?c) -> (?a rdfs:subClassOf ?c)] "));
}
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
// reasoner.setTraceOn(true);
reasoner.setMode(GenericRuleReasoner.HYBRID);
reasoner.setTransitiveClosureCaching(useTGC);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(a, ty, null), new Object[] {
new Triple(a, ty, C1),
new Triple(a, ty, C2),
new Triple(a, ty, C3)
} );
logger.debug("Checkpoint 1");
infgraph.delete(new Triple(C1, sC, C2));
TestUtil.assertIteratorValues(this,
infgraph.find(a, ty, null), new Object[] {
new Triple(a, ty, C1)
} );
logger.debug("Checkpoint 2");
infgraph.add(new Triple(C1, sC, C3));
infgraph.add(new Triple(b, p, C2));
TestUtil.assertIteratorValues(this,
infgraph.find(a, ty, null), new Object[] {
new Triple(a, ty, C1),
new Triple(a, ty, C3)
} );
TestUtil.assertIteratorValues(this,
infgraph.find(b, ty, null), new Object[] {
new Triple(b, ty, C2),
new Triple(b, ty, C3)
} );
TestUtil.assertIteratorValues(this,
data.find(null, null, null), new Object[] {
new Triple(a, p, C1),
new Triple(b, p, C2),
new Triple(C2, sC, C3),
new Triple(C1, sC, C3)
} );
}
/**
* Resolve a bug using remove in rules themselves.
*/
public void testAddRemove2() {
Graph data = Factory.createGraphMem();
data.add(new Triple(a, p, Util.makeIntNode(0)));
List<Rule> rules = Rule.parseRules(
"(?x p ?v)-> (?x q inc(1, a)).\n" +
"(?x p ?v)-> (?x q inc(1, b)).\n" +
"(?x p ?v) (?x q inc(?i, ?t)) noValue(?x r ?t) sum(?v, ?i, ?s) -> remove(0,1), (?x p ?s) (?x r ?t).\n");
// Older version, relied on implicit rule ordering in Jena2.2 not value in 2.3
// "(?x p ?v) noValue(a r 1) -> (?x q inc(1, a)) (?x r 1).\n" +
// "(?x p ?v) noValue(a r 2) -> (?x q inc(1, b)) (?x r 2).\n" +
// "(?x p ?v) (?x q inc(?i, ?t)) sum(?v, ?i, ?s) -> remove(0,1), (?x p ?s).\n");
GenericRuleReasoner reasoner = (GenericRuleReasoner)GenericRuleReasonerFactory.theInstance().create(null);
reasoner.setRules(rules);
reasoner.setMode(GenericRuleReasoner.FORWARD_RETE);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(a, p, null), new Object[] {
new Triple(a, p, Util.makeIntNode(2))
} );
}
}