Source code: com/hp/hpl/jena/enhanced/test/TestPackage.java
1 /*
2 (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
3 [See end of file]
4 $Id: TestPackage.java,v 1.14 2005/02/21 12:03:42 andy_seaborne Exp $
5 */
6 /*
7 * EnhancedTestSuite.java
8 *
9 * Created on 27 November 2002, 04:53
10 */
11
12 package com.hp.hpl.jena.enhanced.test;
13 import com.hp.hpl.jena.mem.*;
14 import com.hp.hpl.jena.graph.*;
15 import com.hp.hpl.jena.graph.test.*;
16 import com.hp.hpl.jena.enhanced.*;
17
18 import junit.framework.*;
19
20 /**
21 * These tests give a small version of a model-like interface
22 {@link TestModel} with different views
23 * over the nodes in the graph {@link TestSubject},
24 *{@link TestProperty} {@link TestObject}
25 *Any node can be any one of these three, but the interface only works
26 *if the node is the subject, property or object, respectively,
27 of some triple in the graph.
28 *There are two implementations of the three interfaces. We use four
29 * different
30 *personalities, in the tests, from various combinations of the implementation
31 *classes with the interface classes. A more realistic test would be a basic set
32 *of interfaces with implementations, and then some more extended interfaces and
33 *implementations which can work together.
34 *
35 *These tests only test EnhNode polymorphism and not EnhGraph polymorphism.
36 *EnhGraph polymorphism currently will not work.
37 *(For Jena2.0 I am imagining that there will be ModelCom and DAMLModelImpl as
38 *the only two implementations, and they can inherit one from the other).
39 * @author jjc
40 */
41 public class TestPackage extends GraphTestBase {
42
43 static final private GraphPersonality split = new GraphPersonality();
44
45 static final private GraphPersonality combo = new GraphPersonality();
46
47
48 static final private GraphPersonality bitOfBoth = new GraphPersonality();
49 static final private GraphPersonality broken = new GraphPersonality();
50 static {
51 // Setting up the personalities, involves registering how
52 // each interface is implemented by default.
53 // Note this does not guarantee that the only implementations
54 // of each interface will be the one specified.
55 // See bitOfBoth.
56 split.add( TestObject.class, TestObjectImpl.factory );
57 split.add( TestSubject.class, TestSubjectImpl.factory );
58 split.add( TestProperty.class, TestPropertyImpl.factory );
59
60 combo.add( TestObject.class, TestAllImpl.factory );
61 combo.add( TestSubject.class, TestAllImpl.factory );
62 combo.add( TestProperty.class, TestAllImpl.factory );
63
64 bitOfBoth.add( TestObject.class, TestObjectImpl.factory );
65 bitOfBoth.add( TestSubject.class, TestSubjectImpl.factory );
66 bitOfBoth.add( TestProperty.class, TestAllImpl.factory );
67
68 // broken is misconfigured and must throw an exception.
69 broken.add(TestObject.class, TestObjectImpl.factory );
70 broken.add( TestSubject.class, TestSubjectImpl.factory );
71 broken.add( TestProperty.class, TestObjectImpl.factory );
72 }
73 /** Creates a new instance of EnhancedTestSuite */
74 public TestPackage(String name)
75 {
76 super( name );
77 }
78
79 public static TestSuite suite()
80 { return new TestSuite( TestPackage.class ); }
81
82 /**
83 test that equals works on an EnhNode (after hedgehog introduced FrontsNode
84 it didn't).
85 */
86 public void testEquals()
87 {
88 EnhNode a = new EnhNode( Node.create( "eg:example" ), null );
89 assertEquals( a, a );
90 }
91
92 /**
93 * View n as intf. This is supported iff rslt.
94 */
95 private static void miniAsSupports(String title, TestNode n, Class intf, boolean rslt ) {
96 assertTrue(title +":sanity",n instanceof Polymorphic);
97
98 // It is always possible to view any node with any interface.
99 TestNode as1 = (TestNode)((EnhNode)n).viewAs(intf);
100 TestNode as2 = (TestNode)((EnhNode)n).viewAs(intf);
101
102 // caching should ensure we get the same result both times.
103 assertTrue( title + ":idempotency", as1==as2 );
104
105 // Whether the interface is actually useable depends on the underlying
106 // graph. This factoid is the rslt parameter.
107 assertEquals( title +":support",rslt,((EnhNode) as1).supports( intf ) );
108 }
109
110 private static void oneNodeAsSupports(String title, TestNode n, boolean rslts[] ) {
111 // Try n with all three interfaces.
112 miniAsSupports(title+"/TestSubject",n,TestSubject.class,rslts[0]);
113 miniAsSupports(title+"/TestProperty",n,TestProperty.class,rslts[1]);
114 miniAsSupports(title+"/TestObject",n,TestObject.class,rslts[2]);
115 }
116
117 private static void manyNodeAsSupports(String title, TestNode n[], boolean rslts[][] ) {
118 // Try each n with each interface.
119 for (int i=0;i<n.length;i++){
120 oneNodeAsSupports(title+"["+i+"]",n[i],rslts[i]);
121 }
122 }
123
124
125 /** This test show the basic format of an enhanced test.
126 * This test access data in an enhanced fashion.
127 * All modifications are done through the underlying graph.
128 * The methods tested are as and supports.
129 */
130 private static void basic(String title, Personality p) {
131 Graph g = new GraphMem();
132 TestModel model = new TestModelImpl(g,p);
133 // create some data
134 graphAdd( g, "x R y;" );
135
136 // The graph has three nodes, extract them as TestNode's,
137 // using the minimalist ModelAPI.
138 TestNode nodes[] = new TestNode[]{
139 model.aSubject(),
140 model.aProperty(),
141 model.anObject()
142 };
143
144 // Run the basic tests.
145 manyNodeAsSupports(title+"(a)",nodes,
146 new boolean[][]{
147 new boolean[]{true,false,false}, // nodes[0] is subj, but not prop, or obj
148 new boolean[]{false,true,false},
149 new boolean[]{false,false,true}
150 });
151
152 graphAdd(g,"y R x;" );
153
154 // The expected results are now different.
155 // (A node is appropriate for the TestSubject interface if it is
156 // the subject of some triple in the graph, so the third node
157 // can now be a TestSubject).
158 manyNodeAsSupports(title+"(b)",nodes,
159 new boolean[][]{
160 new boolean[]{true,false,true}, // nodes[0] is subj and obj, but not prop
161 new boolean[]{false,true,false},
162 new boolean[]{true,false,true}
163 });
164
165 g.delete( triple( "x R y" ) );
166
167 // The expected results are now different again.
168 // (A node is appropriate for the TestSubject interface if it is
169 // the subject of some triple in the graph, so the third node
170 // can now be a TestSubject).
171
172 manyNodeAsSupports(title+"(c)",nodes,
173 new boolean[][]{
174 new boolean[]{false,false,true},
175 new boolean[]{false,true,false},
176 new boolean[]{true,false,false}
177 });
178
179
180 }
181
182 /**
183 Would like to get rid of these, but the abstraction is hard to find at the
184 moment. At least they're now just local to this test class.
185 */
186 static final int S = 1;
187 static final int P = 2;
188 static final int O = 3;
189
190 // This is like the earlier test: miniAsSupports (the last part of it).
191 // However, this time instead of asking whether the interface will work
192 // or not, we just try it.
193 // Obviously sometimes it is broken, which should be reported using
194 // an IllegalStateException.
195 private void canImplement(String title, TestNode n, int wh, boolean rslt ) {
196 try {
197 switch (wh) {
198 case S:
199 n.asSubject().aProperty();
200 break;
201 case P:
202 n.asProperty().anObject();
203 break;
204 case O:
205 n.asObject().aSubject();
206 break;
207 }
208 assertTrue("IllegalStateException expected.",rslt);
209 }
210 catch (IllegalStateException e) {
211 assertFalse("IllegalStateException at the wrong time.",rslt);
212 }
213 }
214
215 private void canImplement(String title, TestNode n, boolean rslts[] ) {
216 canImplement(title+"/TestSubject",n,S,rslts[0]);
217 canImplement(title+"/TestProperty",n,P,rslts[1]);
218 canImplement(title+"/TestObject",n,O,rslts[2]);
219 }
220 private void canImplement(String title, TestNode n[], boolean rslts[][] ) {
221 for (int i=0;i<n.length;i++){
222 canImplement(title+"["+i+"]",n[i],rslts[i]);
223 }
224 }
225
226 private void follow(String title, Personality p) {
227 Graph g = new GraphMem();
228 TestModel model = new TestModelImpl(g,p);
229 // create some data
230 graphAdd( g, "a b c;" );
231 TestNode nodes[] = new TestNode[]{
232 model.aSubject(),
233 model.aProperty(),
234 model.anObject()
235 };
236
237 // Similar to the basic test.
238 canImplement(title+"(a)",nodes,
239 new boolean[][]{
240 new boolean[]{true,false,false},
241 new boolean[]{false,true,false},
242 new boolean[]{false,false,true}
243 });
244
245 graphAdd(g, "b a c;" );
246
247 // Again like in the basic test the triples have now changed,
248 // so different methods will now work.
249 canImplement(title+"(b)",nodes,
250 new boolean[][]{
251 new boolean[]{true,true,false},
252 new boolean[]{true,true,false},
253 new boolean[]{false,false,true}
254 });
255
256 g.delete(triple( "a b c" ) );
257
258
259 // Again like in the basic test the triples have now changed,
260 // so different methods will now work.
261 canImplement(title+"(c)",nodes,
262 new boolean[][]{
263 new boolean[]{false,true,false},
264 new boolean[]{true,false,false},
265 new boolean[]{false,false,true}
266 });
267
268 // Another twist.
269 canImplement(title+"(c)",new TestNode[]{
270 nodes[1].asSubject().aProperty(),
271 nodes[2].asObject().aSubject(),
272 nodes[0].asProperty().anObject()
273 },
274 new boolean[][]{
275 new boolean[]{false,true,false},
276 new boolean[]{true,false,false},
277 new boolean[]{false,false,true}
278 });
279 assertTrue("Model cache test",nodes[0].asProperty().anObject()==nodes[2]);
280 }
281 private void cache(String title, Personality p) {
282 Graph g = new GraphMem();
283 TestModel model = new TestModelImpl(g,p);
284 // create some data
285 graphAdd( g, "a b a;" );
286
287 // get the same node in two different ways.
288 assertTrue("Caching is on",model.aSubject().asObject()==model.anObject());
289
290 ((TestModelImpl)model).getNodeCacheControl().setEnabled(false);
291
292
293 // get the same node in two different ways; if there isn't any caching
294 // then we reconstruct the node.
295 assertFalse("Caching is off",model.aSubject()==model.anObject());
296
297 }
298 public static void testSplitBasic() {
299 basic("Split: ",split);
300 }
301 public static void testComboBasic() {
302 basic("Combo: ",combo);
303 }
304 public void testSplitFollow() {
305 follow("Split: ",split);
306 }
307 public void testComboFollow() {
308 follow("Combo: ",combo);
309 }
310
311 public void testSplitCache() {
312 cache("Split: ",split);
313 }
314 public void testComboCache() {
315 cache("Combo: ",combo);
316 }
317
318 public static void testBitOfBothBasic() {
319 basic("bob: ",bitOfBoth);
320 }
321 public void testBitOfBothFollow() {
322 follow("bob: ",bitOfBoth);
323 }
324
325 public void testBitOfBothCache() {
326 cache("bob: ",bitOfBoth);
327 }
328
329 public static void testBitOfBothSurprise() {
330 // bitOfBoth is a surprising personality ...
331 // we can have two different java objects implementing the same interface.
332
333 Graph g = new GraphMem();
334 TestModel model = new TestModelImpl(g,bitOfBoth);
335 // create some data
336 graphAdd( g, "a a a;" );
337 TestSubject testSubjectImpl = model.aSubject();
338 assertTrue("BitOfBoth makes subjects using TestSubjectImpl",
339 testSubjectImpl instanceof TestSubjectImpl);
340 TestProperty testAllImpl = testSubjectImpl.aProperty();
341 assertTrue("BitOfBoth makes properties using TestAllImpl",
342 testAllImpl instanceof TestAllImpl);
343 assertTrue("turning a TestAllImpl into a TestSubject is a no-op",
344 testAllImpl == testAllImpl.asSubject() );
345 assertTrue("turning a TestAllImpl into a TestSubject is a no-op",
346 testSubjectImpl != testAllImpl.asSubject() );
347 assertTrue("turning a TestAllImpl into a TestSubject is a no-op",
348 testSubjectImpl.asSubject() != testSubjectImpl.asSubject().asProperty().asSubject() );
349
350 }
351
352 public static void testBrokenBasic() {
353 try {
354 // Any of the tests ought to work up and til the point
355 // that they don't. At that point they need to detect the
356 // error and throw the PersonalityConfigException.
357 basic("Broken: ",broken);
358 fail("broken is a misconfigured personality, but it wasn't detected.");
359 }
360 catch (PersonalityConfigException e ) {
361
362 }
363 }
364
365 static class Example
366 {
367 static final Implementation factory = new Implementation()
368 {
369 public EnhNode wrap( Node n, EnhGraph g ) { return new EnhNode( n, g ); }
370
371 public boolean canWrap( Node n, EnhGraph g ) { return n.isURI(); }
372 };
373 }
374
375 public void testSimple()
376 {
377 Graph g = new GraphMem();
378 Personality ours = BuiltinPersonalities.model.copy().add( Example.class, Example.factory );
379 EnhGraph eg = new EnhGraph( g, ours );
380 Node n = Node.createURI( "spoo:bar" );
381 EnhNode eNode = new EnhNode( Node.createURI( "spoo:bar" ), eg );
382 EnhNode eBlank = new EnhNode( Node.createAnon(), eg );
383 assertTrue( "URI node can be an Example", eNode.supports( Example.class ) );
384 assertFalse( "Blank node cannot be an Example", eBlank.supports( Example.class ) );
385 }
386
387 static class AnotherExample
388 {
389 static final Implementation factory = new Implementation()
390 {
391 public EnhNode wrap( Node n, EnhGraph g ) { return new EnhNode( n, g ); }
392
393 public boolean canWrap( Node n, EnhGraph g ) { return n.isURI(); }
394 };
395 }
396
397 public void testAlreadyLinkedViewException()
398 {
399 Graph g = new GraphMem();
400 Personality ours = BuiltinPersonalities.model.copy().add( Example.class, Example.factory );
401 EnhGraph eg = new EnhGraph( g, ours );
402 Node n = Node.create( "spoo:bar" );
403 EnhNode eNode = new EnhNode( n, eg );
404 eNode.viewAs( Example.class );
405 try
406 {
407 eNode.addView( eNode );
408 fail( "should raise an AlreadyLinkedViewException " );
409 }
410 catch (AlreadyLinkedViewException e)
411 {}
412 }
413
414 /**
415 Test that an attempt to polymorph an enhanced node into a class that isn't
416 supported by the enhanced graph generates an UnsupportedPolymorphism
417 exception.
418 */
419 public void testNullPointerTrap()
420 {
421 EnhGraph eg = new EnhGraph( new GraphMem(), BuiltinPersonalities.model );
422 Node n = Node.create( "eh:something" );
423 EnhNode en = new EnhNode( n, eg );
424 try
425 {
426 en.as( TestPackage.class );
427 fail( "oops" );
428 }
429 catch (UnsupportedPolymorphismException e)
430 {
431 assertTrue( "exception should have cuplprit graph", eg == e.getBadGraph() );
432 assertTrue( "exception should have culprit class", TestPackage.class == e.getBadClass() );
433 }
434 }
435
436 }
437
438 /*
439 (c) Copyright 2003, 2004, 2005 Hewlett-Packard Development Company, LP
440 All rights reserved.
441
442 Redistribution and use in source and binary forms, with or without
443 modification, are permitted provided that the following conditions
444 are met:
445
446 1. Redistributions of source code must retain the above copyright
447 notice, this list of conditions and the following disclaimer.
448
449 2. Redistributions in binary form must reproduce the above copyright
450 notice, this list of conditions and the following disclaimer in the
451 documentation and/or other materials provided with the distribution.
452
453 3. The name of the author may not be used to endorse or promote products
454 derived from this software without specific prior written permission.
455
456 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
457 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
458 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
459 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
460 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
461 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
462 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
463 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
464 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
465 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
466 */