001: /*
002: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
003: [See end of file]
004: $Id: AbstractTestQuery.java,v 1.45 2008/01/02 12:08:56 andy_seaborne Exp $
005: */
006:
007: package com.hp.hpl.jena.graph.query.test;
008:
009: import com.hp.hpl.jena.graph.*;
010: import com.hp.hpl.jena.graph.query.*;
011: import com.hp.hpl.jena.shared.JenaException;
012: import com.hp.hpl.jena.shared.QueryStageException;
013: import com.hp.hpl.jena.util.CollectionFactory;
014: import com.hp.hpl.jena.util.iterator.*;
015: import com.hp.hpl.jena.graph.impl.*;
016:
017: import java.util.*;
018: import junit.framework.*;
019:
020: /**
021: Abstract tests for graph query, parameterised on getGraph().
022: @author kers
023: */
024: public abstract class AbstractTestQuery extends QueryTestBase {
025: public AbstractTestQuery(String name) {
026: super (name);
027: }
028:
029: public abstract Graph getGraph();
030:
031: protected Query Q;
032: protected Node O = node("?O");
033: protected Graph empty;
034: protected Graph single;
035:
036: protected final Node[] none = new Node[] {};
037:
038: protected final Node[] justX = new Node[] { Query.X };
039:
040: public static TestSuite suite() {
041: return new TestSuite(QueryTest.class);
042: }
043:
044: public Graph getGraphWith(String facts) {
045: return graphAdd(getGraph(), facts);
046: }
047:
048: public void setUp() {
049: Q = new Query();
050: empty = getGraphWith("");
051: single = getGraphWith("spindizzies drive cities");
052: }
053:
054: private void testTreeQuery(String title, String content,
055: String pattern, String correct) {
056: Graph gc = getGraphWith(content), gp = getGraphWith(pattern);
057: Graph answer = gc.queryHandler().prepareTree(gp).executeTree();
058: if (title.equals(""))
059: title = "checking {" + content + "} against {" + pattern
060: + "} should give {" + correct + "}" + " not "
061: + answer;
062: assertIsomorphic(title, getGraphWith(correct), answer);
063: }
064:
065: private void testTreeQuery(String content, String pattern,
066: String answer) {
067: testTreeQuery("checking", content, pattern, answer);
068: }
069:
070: private static final String[][] tests = {
071: { "", "pigs might fly", "", "" },
072: { "", "", "pigs might fly", "" },
073: { "", "a pings b; b pings c", "a pings _x; _x pings c",
074: "a pings b; b pings c" },
075: { "", "a pings b; b pings c; a pings x; x pings c",
076: "a pings _x; _x pings c",
077: "a pings b; b pings c; a pings x; x pings c" } };
078:
079: public void testManyThings() {
080: for (int i = 0; i < tests.length; i += 1)
081: testTreeQuery(tests[i][0], tests[i][1], tests[i][2],
082: tests[i][3]);
083: }
084:
085: public void testAtomicTreeQuery() {
086: testTreeQuery(
087: "pigs might fly; birds will joke; cats must watch",
088: "birds will joke", "birds will joke");
089: }
090:
091: public void testCompositeTreeQuery() {
092: testTreeQuery(
093: "pigs might fly; birds will joke; cats must watch",
094: "birds will joke; pigs might fly",
095: "pigs might fly; birds will joke");
096: }
097:
098: public void testChainedTreeQuery() {
099: testTreeQuery("a pings b; b pings c; c pings d",
100: "a pings b; b pings c", "a pings b; b pings c");
101: }
102:
103: public void testEmptyIterator() {
104: Graph empty = getGraph();
105: Query q = new Query().addMatch(X, Y, Z);
106: BindingQueryPlan bqp = empty.queryHandler().prepareBindings(q,
107: justX);
108: assertEquals(new HashSet(),
109: iteratorToSet(bqp.executeBindings()));
110: }
111:
112: public void testSingleBindings() {
113: Graph single = getGraphWith("rice grows quickly");
114: Node V1 = node("?v1"), V3 = node("?v3");
115: Query q = new Query().addMatch(V1, node("grows"), V3);
116: BindingQueryPlan qp = single.queryHandler().prepareBindings(q,
117: new Node[] { V1, V3 });
118: assertEquals(nodeListSet("rice quickly"), iteratorToSet(qp
119: .executeBindings()));
120: }
121:
122: public void testMultipleBindings() {
123: Graph several = getGraphWith("rice grows quickly; time isan illusion");
124: Node V1 = node("?v1"), V2 = node("?v2"), V3 = node("?v3");
125: Query q = new Query().addMatch(V1, V2, V3);
126: BindingQueryPlan qp = several.queryHandler().prepareBindings(q,
127: new Node[] { V1, V2, V3 });
128: Set wanted = nodeListSet("time isan illusion; rice grows quickly");
129: assertEquals(wanted, iteratorToSet(qp.executeBindings()));
130: }
131:
132: protected static Set nodeListSet(String s) {
133: Set result = new HashSet();
134: StringTokenizer st = new StringTokenizer(s, ";");
135: while (st.hasMoreTokens())
136: result.add(nodeList(st.nextToken()));
137: return result;
138: }
139:
140: public void testMultiplePatterns() {
141: Graph bookish = getGraphWith("ben wrote Clayface; Starfish ingenre SF; Clayface ingenre Geology; bill wrote Starfish");
142: Query q = new Query();
143: Node A = node("?A");
144: q.addMatch(X, node("wrote"), A).addMatch(A, node("ingenre"),
145: node("SF"));
146: BindingQueryPlan qp = bookish.queryHandler().prepareBindings(q,
147: justX);
148: Set justBill = nodeListSet("bill Starfish");
149: assertEquals(justBill, iteratorToSet(qp.executeBindings()));
150: }
151:
152: /**
153: Utility. Run the query <code>q</code> over the graph <code>g</code>
154: requesting the output variables <code>nodes</code>.
155: */
156: protected ExtendedIterator eb(Graph g, Query q, Node[] nodes) {
157: return g.queryHandler().prepareBindings(q, nodes)
158: .executeBindings();
159: }
160:
161: protected List ebList(Graph g, Query q, Node[] nodes) {
162: return iteratorToList(eb(g, q, nodes));
163: }
164:
165: protected Set ebSet(Graph g, Query q, Node[] nodes) {
166: return iteratorToSet(eb(g, q, nodes));
167: }
168:
169: public void testNodeVariablesA() {
170: Graph mine = getGraphWith("storms hit England");
171: Node spoo = node("?spoo");
172: Q.addMatch(spoo, node("hit"), node("England"));
173: ClosableIterator it = eb(mine, Q, new Node[] { spoo });
174: assertTrue("tnv: it has a solution", it.hasNext());
175: assertEquals("", node("storms"), ((List) it.next()).get(0));
176: assertFalse("tnv: just the one solution", it.hasNext());
177: }
178:
179: public void testNodeVariablesB() {
180: Graph mine = getGraphWith("storms hit England");
181: Node spoo = node("?spoo"), flarn = node("?flarn");
182: Q.addMatch(spoo, node("hit"), flarn);
183: ClosableIterator it = eb(mine, Q, new Node[] { flarn, spoo });
184: assertTrue("tnv: it has a solution", it.hasNext());
185: List answer = (List) it.next();
186: assertEquals("tnvB", node("storms"), answer.get(1));
187: assertEquals("tnvB", node("England"), answer.get(0));
188: assertFalse("tnv: just the one solution", it.hasNext());
189: }
190:
191: public void testBindingQuery() {
192: Graph empty = getGraphWith("");
193: Graph base = getGraphWith("pigs might fly; cats chase mice; dogs chase cars; cats might purr");
194: /* */
195: Query any = new Query().addMatch(Query.ANY, Query.ANY,
196: Query.ANY);
197: assertFalse("empty graph, no bindings", eb(empty, any, none)
198: .hasNext());
199: assertTrue("full graph, > 0 bindings", eb(base, new Query(),
200: none).hasNext());
201: }
202:
203: public void testEmpty() {
204: List bindings = ebList(empty, Q, none);
205: assertEquals(
206: "testEmpty: select [] from {} => 1 empty binding [size]",
207: bindings.size(), 1);
208: Domain d = (Domain) bindings.get(0);
209: assertEquals(
210: "testEmpty: select [] from {} => 1 empty binding [width]",
211: d.size(), 0);
212: }
213:
214: public void testOneMatch() {
215: Q.addMatch(X, Query.ANY, Query.ANY);
216: List bindings = ebList(single, Q, justX);
217: assertEquals(
218: "select X from {spindizzies drive cities} => 1 binding [size]",
219: bindings.size(), 1);
220: Domain d = (Domain) bindings.get(0);
221: assertEquals(
222: "select X from {spindizzies drive cities} => 1 binding [width]",
223: d.size(), 1);
224: assertTrue(
225: "select X from {spindizzies drive cities} => 1 binding [value]",
226: d.get(0).equals(node("spindizzies")));
227: }
228:
229: public void testMismatch() {
230: Q.addMatch(X, X, X);
231: List bindings = ebList(single, Q, justX);
232: assertEquals("bindings mismatch (X X X)", bindings.size(), 0);
233: }
234:
235: public void testXXXMatch1() {
236: Q.addMatch(X, X, X);
237: Graph xxx = getGraphWith("ring ring ring");
238: List bindings = ebList(xxx, Q, justX);
239: assertEquals("bindings match (X X X)", bindings.size(), 1);
240: }
241:
242: public void testXXXMatch3() {
243: Q.addMatch(X, X, X);
244: Graph xxx = getGraphWith("ring ring ring; ding ding ding; ping ping ping");
245: List bindings = ebList(xxx, Q, justX);
246: assertEquals("bindings match (X X X)", bindings.size(), 3);
247: /* */
248: Set found = CollectionFactory.createHashedSet();
249: for (int i = 0; i < bindings.size(); i += 1) {
250: Domain d = (Domain) bindings.get(i);
251: assertEquals("one bound variable", d.size(), 1);
252: found.add(d.get(0));
253: }
254: Set wanted = nodeSet("ring ding ping");
255: assertEquals("testMatch getting {ring ding ping}", found,
256: wanted);
257: }
258:
259: public void testTwoPatterns() {
260: Node reads = node("reads"), inGenre = node("inGenre");
261: Graph g = getGraphWith("chris reads blish; blish inGenre SF");
262: // System.err.println( "| X = " + X + ", Y = " + Y + ", Z = " + Z );
263: Q.addMatch(X, reads, Y);
264: Q.addMatch(Y, inGenre, Z);
265: List bindings = ebList(g, Q, new Node[] { X, Z });
266: assertEquals("testTwoPatterns: one binding", 1, bindings.size());
267: Domain d = (Domain) bindings.get(0);
268: // System.out.println( "* width = " + d.width() );
269: assertTrue("testTwoPatterns: width 2", d.size() >= 2);
270: assertEquals("testTwoPatterns: X = chris", d.get(0),
271: node("chris"));
272: assertEquals("testTwoPatterns: Y = SF", d.get(1), node("SF"));
273: }
274:
275: public void testGraphQuery() {
276: Graph pattern = getGraphWith("?X reads ?Y; ?Y inGenre ?Z");
277: Graph target = getGraphWith("chris reads blish; blish inGenre SF");
278: // System.err.println( "| pattern: " + pattern );
279: Query q = new Query(pattern);
280: List bindings = ebList(target, q, new Node[] { node("?X"),
281: node("?Z") });
282: assertEquals("testTwoPatterns: one binding", 1, bindings.size());
283: Domain d = (Domain) bindings.get(0);
284: // System.out.println( "* width = " + d.width() );
285: assertTrue("testTwoPatterns: width 2", d.size() >= 2);
286: assertEquals("testTwoPatterns: X = chris", d.get(0),
287: node("chris"));
288: assertEquals("testTwoPatterns: Y = SF", d.get(1), node("SF"));
289: }
290:
291: public void testTwoGraphs() {
292: Graph a = getGraphWith("chris reads blish; chris reads norton; chris eats curry");
293: Graph b = getGraphWith("blish inGenre SF; curry inGenre food");
294: Node reads = node("reads"), inGenre = node("inGenre");
295: Q.addMatch("a", X, reads, Y).addMatch("b", Y, inGenre, Z);
296: NamedGraphMap args = Q.args().put("a", a).put("b", b);
297: List bindings = iteratorToList(Q.executeBindings(args,
298: new Node[] { X, Z })); // TODO
299: assertEquals("testTwoGraphs: one binding", 1, bindings.size());
300: Domain d = (Domain) bindings.get(0);
301: assertTrue("testTwoGraphs: width 2", d.size() >= 2);
302: assertEquals("testTwoGraphs: X = chris", d.get(0),
303: node("chris"));
304: assertEquals("testTwoGraphs: Y = SF", d.get(1), node("SF"));
305: }
306:
307: public void testGraphConstraints(String title,
308: Expression constraint, String wanted) {
309: Query Q = new Query().addMatch(Query.ANY, Query.ANY, O)
310: .addConstraint(constraint);
311: Graph G = getGraphWith("pigs fly south; dogs fly badly; plans fly flat");
312: Set results = iteratorToSet(eb(G, Q, new Node[] { O }).mapWith(
313: getFirst));
314: assertEquals("tgs", nodeSet(wanted), results);
315: }
316:
317: public void testGraphConstraints() {
318: Node badly = node("badly"), flat = node("flat");
319: testGraphConstraints("tgs A", Expression.TRUE,
320: "south flat badly");
321: testGraphConstraints("tgs B", notEqual(O, badly), "south flat");
322: testGraphConstraints("tgs C", Dyadic.and(notEqual(O, badly),
323: notEqual(O, flat)), "south");
324: }
325:
326: private void helpConstraint(String title, Expression constraints,
327: int n) {
328: Query q = new Query();
329: Graph g = getGraphWith("blish wrote CIF; blish wrote VOR; hambly wrote Darwath; feynman mechanicked quanta");
330: q.addMatch(X, node("wrote"), Query.ANY);
331: q.addConstraint(constraints);
332: List bindings = ebList(g, q, justX);
333: assertEquals(
334: "testConstraint " + title + ": number of bindings", n,
335: bindings.size());
336: }
337:
338: public void testConstraint() {
339: helpConstraint("none", Expression.TRUE, 3);
340: helpConstraint("X /= blish", notEqual(X, node("blish")), 1);
341: helpConstraint("X /= blish & X /= hambly", Dyadic.and(notEqual(
342: X, node("blish")), notEqual(X, node("hambly"))), 0);
343: }
344:
345: private void helpConstraintThree(String title, Expression c, int n) {
346: Query q = new Query();
347: Graph g = getGraphWith("brust wrote jhereg; hedgehog hacked code; angel age 230; brust wrote 230");
348: q.addConstraint(c);
349: q.addMatch(X, Y, Z);
350: List bindings = ebList(g, q, new Node[] { X, Z });
351: assertEquals(
352: "testConstraint " + title + ": number of bindings", n,
353: bindings.size());
354: }
355:
356: public void testConstraintThree() {
357: helpConstraintThree("testConstraintThree 1:", areEqual(X,
358: node("brust")), 2);
359: helpConstraintThree("testConstraintThree 2:", areEqual(Y,
360: node("hacked")), 1);
361: helpConstraintThree("testConstraintThree 3:", areEqual(Z,
362: node("230")), 2);
363: }
364:
365: public void testConstraintFour() {
366: Query q = new Query();
367: Graph g = getGraphWith("bill pinged ben; ben pinged weed; weed pinged weed; bill ignored bill");
368: q.addMatch(X, node("pinged"), Y);
369: q.addConstraint(notEqual(X, Y));
370: Set bindings = iteratorToSet(eb(g, q, justX).mapWith(getFirst));
371: assertEquals(
372: arrayToSet(new Node[] { node("bill"), node("ben") }),
373: bindings);
374: }
375:
376: /**
377: Test that the MATCHES constraint works.
378: */
379: public void testMatchConstraint() {
380: Set expected = CollectionFactory.createHashedSet();
381: expected.add(node("beta"));
382: Query q = new Query().addMatch(X, node("ppp"), Y)
383: .addConstraint(matches(Y, node("'ell'")));
384: Graph g = getGraphWith("alpha ppp beta; beta ppp 'hello'; gamma ppp 'goodbye'");
385: Set bindings = iteratorToSet(eb(g, q, justX).mapWith(getFirst));
386: assertEquals(expected, bindings);
387: }
388:
389: /**
390: Test that a PatternStage extracts appropriate parts of a constraint set.
391: */
392: public void testExtractConstraint() {
393: // Surely there should be something here?
394: }
395:
396: public void testStringResults() {
397: Graph g = getGraphWith("ding dong dilly");
398: Query q = new Query().addMatch(X, Y, Query.ANY);
399: List bindings = ebList(g, q, new Node[] { X, Y });
400: assertEquals("one result back by name", bindings.size(), 1);
401: assertEquals("x = ding", ((Domain) bindings.get(0)).get(0),
402: node("ding"));
403: }
404:
405: /**
406: this possible failure mode discovered by Andy when building a fast-path
407: RDQL engine over the graph.query SPI.
408: <br>
409: test that we get a sensible result when unbound variables are used in the
410: query result selector.
411: */
412: public void testMissingVariable() {
413: Graph g = getGraphWith("x y z");
414: List bindings = ebList(g, Q, new Node[] { X, Y });
415: List L = (List) bindings.get(0);
416: assertEquals("undefined variables get null", null, L.get(0));
417: }
418:
419: /**
420: More of an example than a test, for a query with "disconnected" triples.
421: */
422: public void testDisconnected() {
423: Graph g = getGraphWith("x pred1 foo; y pred2 bar");
424: Query q = new Query(getGraphWith("?X ?? foo; ?Y ?? bar"));
425: List bindings = ebList(g, q, nodeArray("?X ?Y"));
426: assertEquals(1, bindings.size());
427: assertEquals(node("x"), ((Domain) bindings.get(0)).get(0));
428: assertEquals(node("y"), ((Domain) bindings.get(0)).get(1));
429: }
430:
431: /**
432: Test that the default engine does not re-order triples.
433: */
434: public void testQueryTripleOrder() {
435: Triple t1 = Triple.create("A B C"), t2 = Triple.create("D E F");
436: List desired = Arrays.asList(new Triple[] { t1, t2 });
437: List obtained = getTriplesFromQuery(desired);
438: assertEquals(desired, obtained);
439: }
440:
441: /**
442: This horror to extract the order in which the triples are handed to
443: patternStage illustrates that the Query code needs some refactoring
444: to make it more testable.
445: TODO make the Query code more testable.
446: */
447: private List getTriplesFromQuery(List desired) {
448: Query q = new Query();
449: final Triple[][] tripleses = new Triple[1][];
450: final Graph g = new GraphBase() {
451: public ExtendedIterator graphBaseFind(TripleMatch tm) {
452: return NullIterator.instance;
453: }
454:
455: public QueryHandler queryHandler() {
456: return new SimpleQueryHandler(this ) {
457: public Stage patternStage(Mapping map,
458: ExpressionSet constraints, Triple[] t) {
459: if (t.length > 1)
460: tripleses[0] = t;
461: return super .patternStage(map, constraints, t);
462: }
463: };
464: }
465: };
466: for (int i = 0; i < desired.size(); i += 1)
467: q.addMatch((Triple) desired.get(i));
468: eb(g, q, none);
469: return Arrays.asList(tripleses[0]);
470: }
471:
472: /**
473: test that we can correctly deduce the variable count for some queries.
474: */
475: public void testVariableCount() {
476: assertCount(0, "");
477: assertCount(0, "x R y");
478: assertCount(1, "?x R y");
479: assertCount(1, "?x R y", "?x");
480: assertCount(2, "?x R y", "?z");
481: assertCount(1, "?x R ?x");
482: assertCount(2, "?x R ?y");
483: assertCount(3, "?x R ?y", "?z");
484: assertCount(3, "?x ?R ?y");
485: assertCount(6, "?x ?R ?y; ?a ?S ?c");
486: assertCount(6, "?x ?R ?y; ?a ?S ?c", "?x");
487: assertCount(6, "?x ?R ?y; ?a ?S ?c", "?x ?c");
488: assertCount(6, "?x ?R ?y; ?a ?S ?c", "?x ?y ?c");
489: assertCount(7, "?x ?R ?y; ?a ?S ?c", "?dog");
490: assertCount(8, "?x ?R ?y; ?a ?S ?c", "?dog ?cat ?x");
491: assertCount(18,
492: "?a ?b ?c; ?d ?e ?f; ?g ?h ?i; ?j ?k ?l; ?m ?n ?o; ?p ?q ?r");
493: }
494:
495: public void assertCount(int expected, String query) {
496: assertCount(expected, query, "");
497: }
498:
499: public void assertCount(int expected, String query, String vars) {
500: Graph g = getGraphWith("");
501: Query q = new Query();
502: Triple[] triples = tripleArray(query);
503: for (int i = 0; i < triples.length; i += 1)
504: q.addMatch(triples[i]);
505: // eb( g, q, nodes( vars ) );
506: q.executeBindings(g, nodeArray(vars));
507: assertEquals(expected, q.getVariableCount());
508: }
509:
510: /**
511: test that unbound constraint variables are handled "nicely".
512: */
513: public void testQueryConstraintUnbound() {
514: Query q = new Query().addConstraint(notEqual(X, Z)).addMatch(X,
515: Query.ANY, X);
516: Graph g = getGraphWith("x R x; x R y");
517: try {
518: ExtendedIterator it = eb(g, q, justX);
519: fail("should spot unbound variable");
520: } catch (Query.UnboundVariableException b) {
521: pass();
522: }
523: }
524:
525: public void testCloseQuery() {
526: Graph g = getGraphWith("x R y; a P b; i L j; d X f; h S g; no more heroes");
527: for (int n = 0; n < 1000; n += 1)
528: graphAdd(g, "ping pong X" + n);
529: Query q = new Query().addMatch(Query.S, Query.P, Query.O);
530: List stages = new ArrayList();
531: ExtendedIterator it = eb(g, q, nodeArray("?P"));
532: /* eat one answer to poke pipe */it.next();
533: for (int i = 0; i < stages.size(); i += 1)
534: assertFalse(((Stage) stages.get(i)).isClosed());
535: it.close();
536: for (int i = 0; i < stages.size(); i += 1)
537: assertTrue(((Stage) stages.get(i)).isClosed());
538: }
539:
540: public void testRewriteStartswithExpression() {
541: Query q = new Query();
542: Expression L = constant("x");
543: Expression R = createSimplePattern("^begins");
544: Expression provided = dyadic(L, "Q_StringMatch", R);
545: Expression desired = dyadic(L, "J_startsWith",
546: constant("begins"));
547: q.addConstraint(provided);
548: Expression e2 = (Expression) q.getConstraints().iterator()
549: .next();
550: assertEquals(desired, e2);
551: }
552:
553: public void testRewriteStartswithInsensitiveExpression() {
554: Query q = new Query();
555: Expression L = constant("x");
556: Expression R = createModifiedPattern("^begins", "i");
557: Expression provided = dyadic(L, "Q_StringMatch", R);
558: Expression desired = dyadic(L, "J_startsWithInsensitive",
559: constant("begins"));
560: q.addConstraint(provided);
561: Expression e2 = (Expression) q.getConstraints().iterator()
562: .next();
563: assertEquals(desired, e2);
564: }
565:
566: public void testRewriteEndswithExpression() {
567: Query q = new Query();
568: Expression L = constant("x");
569: Expression R = createSimplePattern("ends$");
570: Expression provided = dyadic(L, "Q_StringMatch", R);
571: Expression desired = dyadic(L, "J_endsWith", constant("ends"));
572: q.addConstraint(provided);
573: Expression e2 = (Expression) q.getConstraints().iterator()
574: .next();
575: assertEquals(desired, e2);
576: }
577:
578: public void testRewriteEndswithInsensitiveExpression() {
579: Query q = new Query();
580: Expression L = constant("x");
581: Expression R = createModifiedPattern("ends$", "i");
582: Expression provided = dyadic(L, "Q_StringMatch", R);
583: Expression desired = dyadic(L, "J_endsWithInsensitive",
584: constant("ends"));
585: q.addConstraint(provided);
586: Expression e2 = (Expression) q.getConstraints().iterator()
587: .next();
588: assertEquals(desired, e2);
589: }
590:
591: public void testRewriteContainsExpression() {
592: Query q = new Query();
593: Expression L = constant("x");
594: Expression R = createSimplePattern("contains");
595: Expression provided = dyadic(L, "Q_StringMatch", R);
596: Expression desired = dyadic(L, "J_contains",
597: constant("contains"));
598: q.addConstraint(provided);
599: Expression e2 = (Expression) q.getConstraints().iterator()
600: .next();
601: assertEquals(desired, e2);
602: }
603:
604: public void testRewritePreservesCharacterCases() {
605: Query q = new Query();
606: Expression L = constant("x");
607: Expression R = createModifiedPattern("coNtaIns", "i");
608: Expression provided = dyadic(L, "Q_StringMatch", R);
609: Expression desired = dyadic(L, "J_containsInsensitive",
610: constant("coNtaIns"));
611: q.addConstraint(provided);
612: Expression e2 = (Expression) q.getConstraints().iterator()
613: .next();
614: assertEquals(desired, e2);
615: }
616:
617: protected static class BangException extends JenaException {
618: public BangException() {
619: super ("bang!");
620: }
621: }
622:
623: public void testQueryExceptionCleanlyExits() {
624: Query q = new Query().addMatch(Triple.ANY);
625: Graph g = new GraphBase() {
626: protected ExtendedIterator graphBaseFind(TripleMatch m) {
627: throw new BangException();
628: }
629: };
630: ExtendedIterator it = eb(g, q, new Node[] {});
631: try {
632: it.next();
633: fail("should fail because graph explodes");
634: } catch (QueryStageException e) {
635: assertTrue(e.getCause() instanceof BangException);
636: } catch (Exception e) {
637: fail("should throw QueryStageException");
638: }
639: }
640:
641: protected static class PL extends Expression.Fixed implements
642: PatternLiteral {
643: protected String modifiers = "";
644:
645: public PL(String content) {
646: super (content);
647: }
648:
649: public PL(String content, String modifiers) {
650: super (content);
651: this .modifiers = modifiers;
652: }
653:
654: public String getPatternString() {
655: return (String) value;
656: }
657:
658: public String getPatternModifiers() {
659: return modifiers;
660: }
661:
662: public String getPatternLanguage() {
663: return rdql;
664: }
665: }
666:
667: public Expression createSimplePattern(final String p) {
668: return new PL(p);
669: }
670:
671: public Expression createModifiedPattern(String content,
672: String modifiers) {
673: return new PL(content, modifiers);
674: }
675:
676: private Expression constant(final Object it) {
677: return new Expression.Fixed(it);
678: }
679:
680: private Expression dyadic(Expression l, String op, Expression r) {
681: final String f = ExpressionFunctionURIs.prefix + op;
682: return new Dyadic(l, f, r) {
683: public boolean evalBool(Object l, Object r) {
684: return false;
685: }
686: };
687: }
688:
689: /**
690: Test that a variety of triple-sorters make no difference to the results of a query
691: over a moderately interesting graph.
692: */
693: public void testTripleSorting() {
694: Graph g = dataGraph();
695: Map answer = getAnswer(g, TripleSorter.dontSort);
696: assertEquals(1, answer.size());
697: assertEquals(new Integer(1), answer.get(Arrays
698: .asList(nodeArray("a d"))));
699: /* */
700: assertEquals(answer, getAnswer(g, TripleSorter.dontSort));
701: assertEquals(answer, getAnswer(g, fiddle(0, 2, 1)));
702: assertEquals(answer, getAnswer(g, fiddle(1, 0, 2)));
703: assertEquals(answer, getAnswer(g, fiddle(1, 2, 0)));
704: assertEquals(answer, getAnswer(g, fiddle(2, 1, 0)));
705: assertEquals(answer, getAnswer(g, fiddle(2, 0, 1)));
706: }
707:
708: protected TripleSorter fiddle(final int a, final int b, final int c) {
709: return new TripleSorter() {
710: public Triple[] sort(Triple[] triples) {
711: return new Triple[] { triples[a], triples[b],
712: triples[c] };
713: }
714: };
715: }
716:
717: protected Graph dataGraph() {
718: Graph result = getGraph();
719: graphAdd(result, "a SPOO d; a X b; b Y c");
720: return result;
721: }
722:
723: protected Map getAnswer(Graph g, TripleSorter sorter) {
724: Map result = CollectionFactory.createHashedMap();
725: Query q = new Query();
726: q.addMatch(triple("?a ?? ?d ")).addMatch(triple("?a X ?b"))
727: .addMatch(triple("?b Y ?c"));
728: q.addConstraint(notEqual(node("?d"), node("?b")));
729: Node[] answers = nodeArray("?a ?d");
730: q.setTripleSorter(sorter);
731: ExtendedIterator it = eb(g, q, answers);
732: while (it.hasNext())
733: addAnswer(result, (List) it.next(), answers.length);
734: return result;
735: }
736:
737: protected void addAnswer(Map result, List bindings, int limit) {
738: List key = bindings.subList(0, limit);
739: Integer already = (Integer) result.get(key);
740: if (already == null)
741: already = new Integer(0);
742: result.put(key, new Integer(already.intValue() + 1));
743: }
744:
745: public void testQueryOptimisation() {
746: int dontCount = queryCount(TripleSorter.dontSort);
747: int optimCount = queryCount(new SimpleTripleSorter());
748: // System.err.println( ">> dontCount=" + dontCount + " optimCount=" + optimCount );
749: if (optimCount > dontCount)
750: fail("optimisation " + optimCount + " yet plain "
751: + dontCount);
752: }
753:
754: public void testFixedTypedLiterals() {
755: Graph g = getGraphWith("a P 'value'xsd:string; b P 'value'xsd:nosuch");
756: if (g.getCapabilities().handlesLiteralTyping()) {
757: Query q = new Query().addMatch(Query.S, Query.P,
758: node("'value'"));
759: ExtendedIterator it = q.executeBindings(g, new Node[] {
760: Query.S, Query.P });
761: assertEquals(nodeSet("a"), iteratorToSet(it
762: .mapWith(select(0))));
763: }
764: }
765:
766: public void testBoundTypedLiterals() {
767: Graph g = getGraphWith("a P 'value'xsd:string; b V 'value'");
768: if (g.getCapabilities().handlesLiteralTyping()) {
769: Query q = new Query().addMatch(node("b"), node("V"),
770: Query.X).addMatch(Query.S, node("P"), Query.X);
771: ExtendedIterator it = q.executeBindings(g, new Node[] {
772: Query.S, Query.P });
773: assertEquals(nodeSet("a"), iteratorToSet(it
774: .mapWith(select(0))));
775: }
776: }
777:
778: int queryCount(TripleSorter sort) {
779: CountingGraph g = bigCountingGraph();
780: for (int a = 0; a < 10; a += 1)
781: for (int b = 0; b < 10; b += 1)
782: for (int X = 0; X < 3; X += 1)
783: graphAdd(g, "a" + a + " X" + (X == 0 ? "" : X + "")
784: + " b" + b);
785: graphAdd(g, "a SPOO d; a X b; b Y c");
786: getAnswer(g, sort);
787: return g.getCount();
788: }
789:
790: static class CountingGraph extends WrappedGraph {
791: int counter;
792: private QueryHandler qh;
793:
794: public QueryHandler queryHandler() {
795: return qh;
796: }
797:
798: CountingGraph(Graph base) {
799: super (base);
800: qh = new SimpleQueryHandler(this );
801: }
802:
803: public ExtendedIterator find(Node s, Node p, Node o) {
804: return find(Triple.createMatch(s, p, o));
805: }
806:
807: public ExtendedIterator find(TripleMatch tm) {
808: return count(base.find(tm));
809: }
810:
811: ExtendedIterator count(ExtendedIterator it) {
812: return new WrappedIterator(it) {
813: public Object next() {
814: try {
815: return super .next();
816: } finally {
817: counter += 1;
818: }
819: }
820: };
821: }
822:
823: int getCount() {
824: return counter;
825: }
826:
827: public String toString() {
828: return base.toString();
829: }
830: }
831:
832: CountingGraph bigCountingGraph() {
833: Graph bigGraph = getGraph();
834: return new CountingGraph(bigGraph);
835: }
836: }
837:
838: /*
839: (c) Copyright 2003, 2004, 2005, 2006, 2007, 2008 Hewlett-Packard Development Company, LP
840: All rights reserved.
841:
842: Redistribution and use in source and binary forms, with or without
843: modification, are permitted provided that the following conditions
844: are met:
845:
846: 1. Redistributions of source code must retain the above copyright
847: notice, this list of conditions and the following disclaimer.
848:
849: 2. Redistributions in binary form must reproduce the above copyright
850: notice, this list of conditions and the following disclaimer in the
851: documentation and/or other materials provided with the distribution.
852:
853: 3. The name of the author may not be used to endorse or promote products
854: derived from this software without specific prior written permission.
855:
856: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
857: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
858: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
859: IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
860: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
861: NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
862: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
863: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
864: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
865: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
866: */
|