blob: 46c3d11f541cb1fff6fdff6119508300ad0af0f5 [file] [log] [blame]
/*
* 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.test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.jena.graph.Factory;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.ontology.OntClass;
import org.apache.jena.ontology.OntModel;
import org.apache.jena.ontology.OntModelSpec;
import org.apache.jena.rdf.model.InfModel;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.NodeIterator;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.ResIterator;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.reasoner.InfGraph;
import org.apache.jena.reasoner.Reasoner;
import org.apache.jena.reasoner.ReasonerFactory;
import org.apache.jena.reasoner.ReasonerRegistry;
import org.apache.jena.reasoner.rulesys.RDFSRuleReasonerFactory ;
import org.apache.jena.reasoner.transitiveReasoner.TransitiveReasoner;
import org.apache.jena.reasoner.transitiveReasoner.TransitiveReasonerFactory;
import org.apache.jena.util.FileManager ;
import org.apache.jena.util.PrintUtil ;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Test cases for transitive reasoner (includes some early RDFS reasoner checks)
*/
public class TestReasoners extends TestCase {
/**
* Boilerplate for junit
*/
public TestReasoners( String name ) {
super( name );
}
/**
* Boilerplate for junit.
* This is its own test suite
*/
public static TestSuite suite() {
return new TestSuite(TestReasoners.class);
}
/**
* Test the basic functioning of a Transitive closure cache
*/
public void testTransitiveReasoner() throws IOException {
ReasonerTester tester = new ReasonerTester("transitive/manifest.rdf");
ReasonerFactory rf = TransitiveReasonerFactory.theInstance();
assertTrue("transitive reasoner tests", tester.runTests(rf, this, null));
}
/**
* Test rebind operation for the transitive reasoner
*/
public void testTransitiveRebind() {
Graph data = Factory.createGraphMem();
Node C1 = NodeFactory.createURI("C1");
Node C2 = NodeFactory.createURI("C2");
Node C3 = NodeFactory.createURI("C3");
Node C4 = NodeFactory.createURI("C4");
data.add( new Triple(C1, RDFS.subClassOf.asNode(), C2) );
data.add( new Triple(C2, RDFS.subClassOf.asNode(), C3) );
Reasoner reasoner = TransitiveReasonerFactory.theInstance().create(null);
assertTrue(reasoner.supportsProperty(RDFS.subClassOf));
assertTrue(! reasoner.supportsProperty(RDFS.domain));
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(C1, null, null),
new Object[] {
new Triple(C1, RDFS.subClassOf.asNode(), C1),
new Triple(C1, RDFS.subClassOf.asNode(), C2),
new Triple(C1, RDFS.subClassOf.asNode(), C3)
} );
Graph data2 = Factory.createGraphMem();
data2.add( new Triple(C1, RDFS.subClassOf.asNode(), C2) );
data2.add( new Triple(C2, RDFS.subClassOf.asNode(), C4) );
infgraph.rebind(data2);
// Incremental additions
Node a = NodeFactory.createURI("a");
Node b = NodeFactory.createURI("b");
Node c = NodeFactory.createURI("c");
infgraph.add(new Triple(a, RDFS.subClassOf.asNode(), b));
infgraph.add(new Triple(b, RDFS.subClassOf.asNode(), c));
TestUtil.assertIteratorValues(this,
infgraph.find(b, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(b, RDFS.subClassOf.asNode(), c),
new Triple(b, RDFS.subClassOf.asNode(), b)
} );
TestUtil.assertIteratorValues(this,
infgraph.find(a, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(a, RDFS.subClassOf.asNode(), a),
new Triple(a, RDFS.subClassOf.asNode(), b),
new Triple(a, RDFS.subClassOf.asNode(), c)
} );
Node p = NodeFactory.createURI("p");
Node q = NodeFactory.createURI("q");
Node r = NodeFactory.createURI("r");
infgraph.add(new Triple(p, RDFS.subPropertyOf.asNode(), q));
infgraph.add(new Triple(q, RDFS.subPropertyOf.asNode(), r));
TestUtil.assertIteratorValues(this,
infgraph.find(q, RDFS.subPropertyOf.asNode(), null),
new Object[] {
new Triple(q, RDFS.subPropertyOf.asNode(), q),
new Triple(q, RDFS.subPropertyOf.asNode(), r)
} );
TestUtil.assertIteratorValues(this,
infgraph.find(p, RDFS.subPropertyOf.asNode(), null),
new Object[] {
new Triple(p, RDFS.subPropertyOf.asNode(), p),
new Triple(p, RDFS.subPropertyOf.asNode(), q),
new Triple(p, RDFS.subPropertyOf.asNode(), r)
} );
}
/**
* Test delete operation for Transtive reasoner.
*/
public void testTransitiveRemove() {
Graph data = Factory.createGraphMem();
Node a = NodeFactory.createURI("a");
Node b = NodeFactory.createURI("b");
Node c = NodeFactory.createURI("c");
Node d = NodeFactory.createURI("d");
Node e = NodeFactory.createURI("e");
Node closedP = RDFS.subClassOf.asNode();
data.add( new Triple(a, RDFS.subClassOf.asNode(), b) );
data.add( new Triple(a, RDFS.subClassOf.asNode(), c) );
data.add( new Triple(b, RDFS.subClassOf.asNode(), d) );
data.add( new Triple(c, RDFS.subClassOf.asNode(), d) );
data.add( new Triple(d, RDFS.subClassOf.asNode(), e) );
Reasoner reasoner = TransitiveReasonerFactory.theInstance().create(null);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this, infgraph.find(a, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(a, closedP, a),
new Triple(a, closedP, b),
new Triple(a, closedP, b),
new Triple(a, closedP, c),
new Triple(a, closedP, d),
new Triple(a, closedP, e)
});
TestUtil.assertIteratorValues(this, infgraph.find(b, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(b, closedP, b),
new Triple(b, closedP, d),
new Triple(b, closedP, e)
});
infgraph.delete(new Triple(b, closedP, d));
TestUtil.assertIteratorValues(this, infgraph.find(a, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(a, closedP, a),
new Triple(a, closedP, b),
new Triple(a, closedP, b),
new Triple(a, closedP, c),
new Triple(a, closedP, d),
new Triple(a, closedP, e)
});
TestUtil.assertIteratorValues(this, infgraph.find(b, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(b, closedP, b),
});
infgraph.delete(new Triple(a, closedP, c));
TestUtil.assertIteratorValues(this, infgraph.find(a, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(a, closedP, a),
new Triple(a, closedP, b)
});
TestUtil.assertIteratorValues(this, infgraph.find(b, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(b, closedP, b)
});
TestUtil.assertIteratorValues(this, data.find(null, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(a, closedP, b),
new Triple(c, closedP, d),
new Triple(d, closedP, e)
});
}
/**
* Test metalevel add/remove subproperty operations for transitive reasoner.
*/
public void testTransitiveMetaLevel() {
doTestMetaLevel(TransitiveReasonerFactory.theInstance());
}
/**
* Test metalevel add/remove subproperty operations for rdsf reasoner.
*/
public void testRDFSMetaLevel() {
doTestMetaLevel(RDFSRuleReasonerFactory.theInstance());
}
/**
* Test metalevel add/remove subproperty operations for a reasoner.
*/
public void doTestMetaLevel(ReasonerFactory rf) {
Graph data = Factory.createGraphMem();
Node c1 = NodeFactory.createURI("C1");
Node c2 = NodeFactory.createURI("C2");
Node c3 = NodeFactory.createURI("C3");
Node p = NodeFactory.createURI("p");
Node q = NodeFactory.createURI("q");
Node sC = RDFS.subClassOf.asNode();
Node sP = RDFS.subPropertyOf.asNode();
data.add( new Triple(c2, sC, c3));
data.add( new Triple(c1, p, c2));
Reasoner reasoner = rf.create(null);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null),
new Object[] {
});
infgraph.add(new Triple(p, q, sC));
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null),
new Object[] {
});
infgraph.add(new Triple(q, sP, sP));
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null),
new Object[] {
new Triple(c1, sC, c1),
new Triple(c1, sC, c2),
new Triple(c1, sC, c3)
});
infgraph.delete(new Triple(p, q, sC));
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null),
new Object[] {
});
}
/**
* Check a complex graph's transitive reduction.
*/
public void testTransitiveReduction() {
Model test = FileManager.getInternal().loadModelInternal("testing/reasoners/bugs/subpropertyModel.n3");
Property dp = test.getProperty(TransitiveReasoner.directSubPropertyOf.getURI());
doTestTransitiveReduction(test, dp);
}
/**
* Test that a transitive reduction is complete.
* Assumes test graph has no cycles (other than the trivial
* identity ones).
*/
public void doTestTransitiveReduction(Model model, Property dp) {
InfModel im = ModelFactory.createInfModel(ReasonerRegistry.getTransitiveReasoner(), model);
for (ResIterator i = im.listSubjects(); i.hasNext();) {
Resource base = i.nextResource();
List<RDFNode> directLinks = new ArrayList<>();
for (NodeIterator j = im.listObjectsOfProperty(base, dp); j.hasNext(); ) {
directLinks.add(j.next());
}
for (int n = 0; n < directLinks.size(); n++) {
Resource d1 = (Resource)directLinks.get(n);
for (int m = n+1; m < directLinks.size(); m++) {
Resource d2 = (Resource)directLinks.get(m);
if (im.contains(d1, dp, d2) && ! base.equals(d1) && !base.equals(d2)) {
assertTrue("Triangle discovered in transitive reduction", false);
}
}
}
}
}
/**
* The reasoner contract for bind(data) is not quite precise. It allows for
* reasoners which have state so that reusing the same reasoner on a second data
* model might lead to interference. This in fact used to happen with the transitive
* reasoner. This is a test to check the top level symptoms of this which can be
* solved just be not reusing reasoners.
* @todo this test might be better moved to OntModel tests somewhere
*/
public void testTransitiveSpecReuse() {
OntModel om1 = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM_TRANS_INF);
Resource c1 = om1.createResource(PrintUtil.egNS + "Class1");
Resource c2 = om1.createResource(PrintUtil.egNS + "Class2");
Resource c3 = om1.createResource(PrintUtil.egNS + "Class3");
om1.add(c1, RDFS.subClassOf, c2);
om1.add(c2, RDFS.subClassOf, c3);
om1.prepare();
assertFalse(om1.isEmpty());
OntModel om2 = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM_TRANS_INF);
StmtIterator si = om2.listStatements();
boolean ok = ! si.hasNext();
si.close();
assertTrue("Transitive reasoner state leak", ok);
}
/**
* The reasoner contract for bind(data) is not quite precise. It allows for
* reasoners which have state so that reusing the same reasoner on a second data
* model might lead to interference. This in fact used to happen with the transitive
* reasoner. This is a test to check that the transitive reasoner state reuse has been fixed at source.
*/
public void testTransitiveBindReuse() {
Reasoner r = ReasonerRegistry.getTransitiveReasoner();
InfModel om1 = ModelFactory.createInfModel(r, ModelFactory.createDefaultModel());
Resource c1 = om1.createResource(PrintUtil.egNS + "Class1");
Resource c2 = om1.createResource(PrintUtil.egNS + "Class2");
Resource c3 = om1.createResource(PrintUtil.egNS + "Class3");
om1.add(c1, RDFS.subClassOf, c2);
om1.add(c2, RDFS.subClassOf, c3);
om1.prepare();
assertFalse(om1.isEmpty());
InfModel om2 = ModelFactory.createInfModel(r, ModelFactory.createDefaultModel());
StmtIterator si = om2.listStatements();
boolean ok = ! si.hasNext();
si.close();
assertTrue("Transitive reasoner state leak", ok);
}
/**
* Test that two transitive engines are independent.
* See JENA-1260
*/
public void testTransitiveEngineSeparation() throws InterruptedException {
String NS = "http://example.com/test#";
Property sp = ResourceFactory.createProperty(NS, "sp");
Property p = ResourceFactory.createProperty(NS, "p");
Property s = ResourceFactory.createProperty(NS, "s");
Resource q = ResourceFactory.createProperty(NS, "q");
Reasoner reasoner = ReasonerRegistry.getTransitiveReasoner();
InfModel simple = ModelFactory.createInfModel(reasoner, ModelFactory.createDefaultModel());
simple.add(s, sp, p);
assertFalse( simple.contains(s, RDFS.subPropertyOf, p) );
InfModel withSP = ModelFactory.createInfModel(reasoner, ModelFactory.createDefaultModel());
withSP.add(sp, RDFS.subPropertyOf, RDFS.subPropertyOf);
withSP.add(s, sp, p);
assertTrue( withSP.contains(s, RDFS.subPropertyOf, p) );
simple.add(q, sp, p);
assertFalse( simple.contains(q, RDFS.subPropertyOf, p) );
}
/**
* Test rebind operation for the RDFS reasoner
*/
public void testRDFSRebind() {
Graph data = Factory.createGraphMem();
Node C1 = NodeFactory.createURI("C1");
Node C2 = NodeFactory.createURI("C2");
Node C3 = NodeFactory.createURI("C3");
Node C4 = NodeFactory.createURI("C4");
data.add( new Triple(C1, RDFS.subClassOf.asNode(), C2) );
data.add( new Triple(C2, RDFS.subClassOf.asNode(), C3) );
Reasoner reasoner = RDFSRuleReasonerFactory.theInstance().create(null);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this,
infgraph.find(C1, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(C1, RDFS.subClassOf.asNode(), C1),
new Triple(C1, RDFS.subClassOf.asNode(), C2),
new Triple(C1, RDFS.subClassOf.asNode(), C3)
} );
Graph data2 = Factory.createGraphMem();
data2.add( new Triple(C1, RDFS.subClassOf.asNode(), C2) );
data2.add( new Triple(C2, RDFS.subClassOf.asNode(), C4) );
infgraph.rebind(data2);
TestUtil.assertIteratorValues(this,
infgraph.find(C1, RDFS.subClassOf.asNode(), null),
new Object[] {
new Triple(C1, RDFS.subClassOf.asNode(), C1),
new Triple(C1, RDFS.subClassOf.asNode(), C2),
new Triple(C1, RDFS.subClassOf.asNode(), C4)
} );
}
/**
* Test remove operations on an RDFS reasoner instance.
* This is an example to test that rebing is invoked correctly rather
* than an RDFS-specific test.
*/
public void testRDFSRemove() {
InfModel m = ModelFactory.createRDFSModel(ModelFactory.createDefaultModel());
String NS = PrintUtil.egNS;
Property p = m.createProperty(NS, "p");
Resource D = m.createResource(NS + "D");
Resource i = m.createResource(NS + "i");
Resource c = m.createResource(NS + "c");
Resource d = m.createResource(NS + "d");
p.addProperty(RDFS.domain, D);
i.addProperty(p, c);
i.addProperty(p, d);
TestUtil.assertIteratorValues(this, i.listProperties(), new Object[] {
m.createStatement(i, p, c),
m.createStatement(i, p, d),
m.createStatement(i, RDF.type, D),
m.createStatement(i, RDF.type, RDFS.Resource),
});
i.removeAll(p);
TestUtil.assertIteratorValues(this, i.listProperties(), new Object[] {
});
}
/**
* Cycle bug in transitive reasoner
*/
public void testTransitiveCycleBug() {
Model m = FileManager.getInternal().loadModelInternal( "file:testing/reasoners/bugs/unbroken.n3" );
OntModel om = ModelFactory.createOntologyModel( OntModelSpec.RDFS_MEM_TRANS_INF, m );
OntClass rootClass = om.getOntClass( RDFS.Resource.getURI() );
Resource c = m.getResource("c");
Set<OntClass> direct = rootClass.listSubClasses( true ).toSet();
assertFalse( direct.contains( c ) );
}
/**
* Test the ModelFactory interface
*/
public void testModelFactoryRDFS() {
Model data = ModelFactory.createDefaultModel();
Property p = data.createProperty("urn:x-hp:ex/p");
Resource a = data.createResource("urn:x-hp:ex/a");
Resource b = data.createResource("urn:x-hp:ex/b");
Resource C = data.createResource("urn:x-hp:ex/c");
data.add(p, RDFS.range, C)
.add(a, p, b);
Model result = ModelFactory.createRDFSModel(data);
StmtIterator i = result.listStatements( b, RDF.type, (RDFNode)null );
TestUtil.assertIteratorValues(this, i, new Object[] {
data.createStatement(b, RDF.type, RDFS.Resource ),
data.createStatement(b, RDF.type, C )
});
}
/**
* Run test on findWithPremies for Transitive reasoner.
*/
public void testTransitiveFindWithPremises() {
doTestFindWithPremises(TransitiveReasonerFactory.theInstance());
}
/**
* Run test on findWithPremies for RDFS reasoner.
*/
public void testRDFSFindWithPremises() {
doTestFindWithPremises(RDFSRuleReasonerFactory.theInstance());
}
/**
* Test a reasoner's ability to implement find with premises.
* Assumes the reasoner can at least implement RDFS subClassOf.
*/
public void doTestFindWithPremises(ReasonerFactory rf) {
Node c1 = NodeFactory.createURI("C1");
Node c2 = NodeFactory.createURI("C2");
Node c3 = NodeFactory.createURI("C3");
Node sC = RDFS.subClassOf.asNode();
Graph data = Factory.createGraphMem();
data.add( new Triple(c2, sC, c3));
Graph premise = Factory.createGraphMem();
premise.add( new Triple(c1, sC, c2));
Reasoner reasoner = rf.create(null);
InfGraph infgraph = reasoner.bind(data);
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null),
new Object[] {
});
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null, premise),
new Object[] {
new Triple(c1, sC, c2),
new Triple(c1, sC, c3),
new Triple(c1, sC, c1)
});
TestUtil.assertIteratorValues(this, infgraph.find(c1, sC, null),
new Object[] {
});
}
}