/* * 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. */ /* * EnhancedTestSuite.java * * Created on 27 November 2002, 04:53 */ package org.apache.jena.enhanced.test; import junit.framework.*; import org.apache.jena.enhanced.* ; import org.apache.jena.graph.* ; import org.apache.jena.graph.test.* ; import org.apache.jena.rdf.model.* ; import org.apache.jena.shared.JenaException ; /** * These tests give a small version of a model-like interface {@link TestModel} with different views * over the nodes in the graph {@link TestSubject}, *{@link TestProperty} {@link TestObject} *Any node can be any one of these three, but the interface only works *if the node is the subject, property or object, respectively, of some triple in the graph. *There are two implementations of the three interfaces. We use four * different *personalities, in the tests, from various combinations of the implementation *classes with the interface classes. A more realistic test would be a basic set *of interfaces with implementations, and then some more extended interfaces and *implementations which can work together. * *These tests only test EnhNode polymorphism and not EnhGraph polymorphism. *EnhGraph polymorphism currently will not work. */ public class TestPackage extends GraphTestBase { static final private Personality<RDFNode> split = new Personality<>(); static final private Personality<RDFNode> combo = new Personality<>(); static final private GraphPersonality bitOfBoth = new GraphPersonality(); static final private GraphPersonality broken = new GraphPersonality(); static { // Setting up the personalities, involves registering how // each interface is implemented by default. // Note this does not guarantee that the only implementations // of each interface will be the one specified. // See bitOfBoth. split.add( TestObject.class, TestObjectImpl.factory ); split.add( TestSubject.class, TestSubjectImpl.factory ); split.add( TestProperty.class, TestPropertyImpl.factory ); combo.add( TestObject.class, TestAllImpl.factory ); combo.add( TestSubject.class, TestAllImpl.factory ); combo.add( TestProperty.class, TestAllImpl.factory ); bitOfBoth.add( TestObject.class, TestObjectImpl.factory ); bitOfBoth.add( TestSubject.class, TestSubjectImpl.factory ); bitOfBoth.add( TestProperty.class, TestAllImpl.factory ); // broken is misconfigured and must throw an exception. broken.add(TestObject.class, TestObjectImpl.factory ); broken.add( TestSubject.class, TestSubjectImpl.factory ); broken.add( TestProperty.class, TestObjectImpl.factory ); } /** Creates a new instance of EnhancedTestSuite */ public TestPackage(String name) { super( name ); } public static TestSuite suite() { return new TestSuite( TestPackage.class ); } /** test that equals works on an EnhNode (after hedgehog introduced FrontsNode it didn't). */ public void testEquals() { EnhNode a = new EnhNode( NodeCreateUtils.create( "eg:example" ), null ); assertEquals( a, a ); } /** * View n as intf. This is supported iff rslt. */ private static <X extends RDFNode> void miniAsSupports(String title, TestNode n, Class<X> intf, boolean rslt ) { assertTrue(title +":sanity",n instanceof Polymorphic<?>); // It is always possible to view any node with any interface. TestNode as1 = (TestNode)((EnhNode)n).viewAs(intf); TestNode as2 = (TestNode)((EnhNode)n).viewAs(intf); // caching should ensure we get the same result both times. assertTrue( title + ":idempotency", as1==as2 ); // Whether the interface is actually useable depends on the underlying // graph. This factoid is the rslt parameter. assertEquals( title +":support",rslt,((EnhNode) as1).supports( intf ) ); } private static void oneNodeAsSupports(String title, TestNode n, boolean rslts[] ) { // Try n with all three interfaces. miniAsSupports(title+"/TestSubject",n,TestSubject.class,rslts[0]); miniAsSupports(title+"/TestProperty",n,TestProperty.class,rslts[1]); miniAsSupports(title+"/TestObject",n,TestObject.class,rslts[2]); } private static void manyNodeAsSupports(String title, TestNode n[], boolean rslts[][] ) { // Try each n with each interface. for (int i=0;i<n.length;i++){ oneNodeAsSupports(title+"["+i+"]",n[i],rslts[i]); } } /** This test show the basic format of an enhanced test. * This test access data in an enhanced fashion. * All modifications are done through the underlying graph. * The methods tested are as and supports. */ private static void basic(String title, Personality<RDFNode> p) { Graph g = Factory.createGraphMem(); TestModel model = new TestModelImpl(g,p); // create some data graphAdd( g, "x R y;" ); // The graph has three nodes, extract them as TestNode's, // using the minimalist ModelAPI. TestNode nodes[] = new TestNode[]{ model.aSubject(), model.aProperty(), model.anObject() }; // Run the basic tests. manyNodeAsSupports(title+"(a)",nodes, new boolean[][]{ new boolean[]{true,false,false}, // nodes[0] is subj, but not prop, or obj new boolean[]{false,true,false}, new boolean[]{false,false,true} }); graphAdd(g,"y R x;" ); // The expected results are now different. // (A node is appropriate for the TestSubject interface if it is // the subject of some triple in the graph, so the third node // can now be a TestSubject). manyNodeAsSupports(title+"(b)",nodes, new boolean[][]{ new boolean[]{true,false,true}, // nodes[0] is subj and obj, but not prop new boolean[]{false,true,false}, new boolean[]{true,false,true} }); g.delete( triple( "x R y" ) ); // The expected results are now different again. // (A node is appropriate for the TestSubject interface if it is // the subject of some triple in the graph, so the third node // can now be a TestSubject). manyNodeAsSupports(title+"(c)",nodes, new boolean[][]{ new boolean[]{false,false,true}, new boolean[]{false,true,false}, new boolean[]{true,false,false} }); } /** Would like to get rid of these, but the abstraction is hard to find at the moment. At least they're now just local to this test class. */ static final int S = 1; static final int P = 2; static final int O = 3; // This is like the earlier test: miniAsSupports (the last part of it). // However, this time instead of asking whether the interface will work // or not, we just try it. // Obviously sometimes it is broken, which should be reported using // an IllegalStateException. private void canImplement(String title, TestNode n, int wh, boolean rslt ) { try { switch (wh) { case S: n.asSubject().aProperty(); break; case P: n.asProperty().anObject(); break; case O: n.asObject().aSubject(); break; } assertTrue("IllegalStateException expected.",rslt); } catch (IllegalStateException e) { assertFalse("IllegalStateException at the wrong time.",rslt); } } private void canImplement(String title, TestNode n, boolean rslts[] ) { canImplement(title+"/TestSubject",n,S,rslts[0]); canImplement(title+"/TestProperty",n,P,rslts[1]); canImplement(title+"/TestObject",n,O,rslts[2]); } private void canImplement(String title, TestNode n[], boolean rslts[][] ) { for (int i=0;i<n.length;i++){ canImplement(title+"["+i+"]",n[i],rslts[i]); } } private void follow(String title, Personality<RDFNode> p) { Graph g = Factory.createGraphMem(); TestModel model = new TestModelImpl(g,p); // create some data graphAdd( g, "a b c;" ); TestNode nodes[] = new TestNode[]{ model.aSubject(), model.aProperty(), model.anObject() }; // Similar to the basic test. canImplement(title+"(a)",nodes, new boolean[][]{ new boolean[]{true,false,false}, new boolean[]{false,true,false}, new boolean[]{false,false,true} }); graphAdd(g, "b a c;" ); // Again like in the basic test the triples have now changed, // so different methods will now work. canImplement(title+"(b)",nodes, new boolean[][]{ new boolean[]{true,true,false}, new boolean[]{true,true,false}, new boolean[]{false,false,true} }); g.delete(triple( "a b c" ) ); // Again like in the basic test the triples have now changed, // so different methods will now work. canImplement(title+"(c)",nodes, new boolean[][]{ new boolean[]{false,true,false}, new boolean[]{true,false,false}, new boolean[]{false,false,true} }); // Another twist. canImplement(title+"(c)",new TestNode[]{ nodes[1].asSubject().aProperty(), nodes[2].asObject().aSubject(), nodes[0].asProperty().anObject() }, new boolean[][]{ new boolean[]{false,true,false}, new boolean[]{true,false,false}, new boolean[]{false,false,true} }); assertTrue("Model cache test",nodes[0].asProperty().anObject()==nodes[2]); } public static void testSplitBasic() { basic("Split: ",split); } public static void testComboBasic() { basic("Combo: ",combo); } public void testSplitFollow() { follow("Split: ",split); } public void testComboFollow() { follow("Combo: ",combo); } public static void testBitOfBothBasic() { basic("bob: ",bitOfBoth); } public void testBitOfBothFollow() { follow("bob: ",bitOfBoth); } public static void testBitOfBothSurprise() { // bitOfBoth is a surprising personality ... // we can have two different java objects implementing the same interface. Graph g = Factory.createGraphMem(); TestModel model = new TestModelImpl(g,bitOfBoth); // create some data graphAdd( g, "a a a;" ); TestSubject testSubjectImpl = model.aSubject(); assertTrue("BitOfBoth makes subjects using TestSubjectImpl", testSubjectImpl instanceof TestSubjectImpl); TestProperty testAllImpl = testSubjectImpl.aProperty(); assertTrue("BitOfBoth makes properties using TestAllImpl", testAllImpl instanceof TestAllImpl); assertTrue("turning a TestAllImpl into a TestSubject is a no-op", testAllImpl == testAllImpl.asSubject() ); assertTrue("turning a TestAllImpl into a TestSubject is a no-op", testSubjectImpl != testAllImpl.asSubject() ); assertTrue("turning a TestAllImpl into a TestSubject is a no-op", testSubjectImpl.asSubject() != testSubjectImpl.asSubject().asProperty().asSubject() ); } public static void testBrokenBasic() { try { // Any of the tests ought to work up and til the point // that they don't. At that point they need to detect the // error and throw the PersonalityConfigException. basic("Broken: ",broken); fail("broken is a misconfigured personality, but it wasn't detected."); } catch (PersonalityConfigException e ) { } } static class Example extends EnhNode implements RDFNode { public Example( Node n, EnhGraph g ) { super( n, g ); } static final Implementation factory = new Implementation() { @Override public EnhNode wrap( Node n, EnhGraph g ) { return new EnhNode( n, g ); } @Override public boolean canWrap( Node n, EnhGraph g ) { return n.isURI(); } }; @Override public RDFNode inModel( Model m ) { return null; } @Override public Model getModel() { throw new JenaException( "getModel() should not be called in the EnhGraph/Node tests" ); } @Override public Resource asResource() { throw new JenaException( "asResource() should not be called in the EnhGraph/Node tests" ); } @Override public Literal asLiteral() { throw new JenaException( "asLiteral() should not be called in the EnhGraph/Node tests" ); } @Override public Object visitWith( RDFVisitor rv ) { return null; } } public void testSimple() { Graph g = Factory.createGraphMem(); Personality<RDFNode> ours = BuiltinPersonalities.model.copy().add( Example.class, Example.factory ); EnhGraph eg = new EnhGraph( g, ours ); Node n = NodeFactory.createURI( "spoo:bar" ); EnhNode eNode = new EnhNode( NodeFactory.createURI( "spoo:bar" ), eg ); EnhNode eBlank = new EnhNode( NodeFactory.createBlankNode(), eg ); assertTrue( "URI node can be an Example", eNode.supports( Example.class ) ); assertFalse( "Blank node cannot be an Example", eBlank.supports( Example.class ) ); } static class AnotherExample { static final Implementation factory = new Implementation() { @Override public EnhNode wrap( Node n, EnhGraph g ) { return new EnhNode( n, g ); } @Override public boolean canWrap( Node n, EnhGraph g ) { return n.isURI(); } }; } public void testAlreadyLinkedViewException() { Graph g = Factory.createGraphMem(); Personality<RDFNode> ours = BuiltinPersonalities.model.copy().add( Example.class, Example.factory ); EnhGraph eg = new EnhGraph( g, ours ); Node n = NodeCreateUtils.create( "spoo:bar" ); EnhNode eNode = new Example( n, eg ); EnhNode multiplexed = new Example( n, eg ); multiplexed.as( Property.class ); eNode.viewAs( Example.class ); try { eNode.addView( multiplexed ); fail( "should raise an AlreadyLinkedViewException " ); } catch (AlreadyLinkedViewException e) {} } /** Test that an attempt to polymorph an enhanced node into a class that isn't supported by the enhanced graph generates an UnsupportedPolymorphism exception. */ public void testNullPointerTrap() { EnhGraph eg = new EnhGraph( Factory.createGraphMem(), new Personality<RDFNode>() ); Node n = NodeCreateUtils.create( "eh:something" ); EnhNode en = new EnhNode( n, eg ); try { en.as( Property.class ); fail( "oops" ); } catch (UnsupportedPolymorphismException e) { assertEquals( en, e.getBadNode() ); assertTrue( "exception should have cuplprit graph", eg == ((EnhNode)e.getBadNode()).getGraph() ); assertSame( "exception should have culprit class", Property.class, e.getBadClass() ); } } public void testNullPointerTrapInCanSupport() { EnhGraph eg = new EnhGraph( Factory.createGraphMem(), new Personality<RDFNode>() ); Node n = NodeCreateUtils.create( "eh:something" ); EnhNode en = new EnhNode( n, eg ); assertFalse( en.canAs( Property.class ) ); } public void testAsToOwnClassWithNoModel() { Resource r = ResourceFactory.createResource(); assertEquals( null, r.getModel() ); assertTrue( r.canAs( Resource.class ) ); assertSame( r, r.as( Resource.class ) ); } public void testCanAsReturnsFalseIfNoModel() { Resource r = ResourceFactory.createResource(); assertEquals( false, r.canAs( Example.class ) ); } public void testAsThrowsPolymorphismExceptionIfNoModel() { Resource r = ResourceFactory.createResource(); try { r.as( Example.class ); fail( "should throw UnsupportedPolymorphismException" ); } catch (UnsupportedPolymorphismException e) { assertTrue( e.getBadNode() instanceof EnhNode ); assertEquals( null, ((EnhNode)e.getBadNode()).getGraph() ); assertEquals( Example.class, e.getBadClass() ); } } }