Fast generic function dispatch without calling `compile' at runtime
[bpt/guile.git] / test-suite / tests / sxml.xpath.test
1 ;;;; sxml.xpath.test -*- scheme -*-
2 ;;;;
3 ;;;; Copyright (C) 2010 Free Software Foundation, Inc.
4 ;;;; Copyright (C) 2001,2002,2003,2004 Oleg Kiselyov <oleg at pobox dot com>
5 ;;;;
6 ;;;; This library is free software; you can redistribute it and/or
7 ;;;; modify it under the terms of the GNU Lesser General Public
8 ;;;; License as published by the Free Software Foundation; either
9 ;;;; version 3 of the License, or (at your option) any later version.
10 ;;;;
11 ;;;; This library is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;;;; Lesser General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU Lesser General Public
17 ;;;; License along with this library; if not, write to the Free Software
18 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 ;;; Commentary:
21 ;;
22 ;; Unit tests for (sxml xpath).
23 ;;
24 ;;; Code:
25
26 (define-module (test-suite sxml-xpath)
27 #:use-module (test-suite lib)
28 #:use-module (sxml xpath))
29
30 (define tree1
31 '(html
32 (head (title "Slides"))
33 (body
34 (p (@ (align "center"))
35 (table (@ (style "font-size: x-large"))
36 (tr
37 (td (@ (align "right")) "Talks ")
38 (td (@ (align "center")) " = ")
39 (td " slides + transition"))
40 (tr (td)
41 (td (@ (align "center")) " = ")
42 (td " data + control"))
43 (tr (td)
44 (td (@ (align "center")) " = ")
45 (td " programs"))))
46 (ul
47 (li (a (@ (href "slides/slide0001.gif")) "Introduction"))
48 (li (a (@ (href "slides/slide0010.gif")) "Summary")))
49 )))
50
51
52 ;; Example from a posting "Re: DrScheme and XML",
53 ;; Shriram Krishnamurthi, comp.lang.scheme, Nov. 26. 1999.
54 ;; http://www.deja.com/getdoc.xp?AN=553507805
55 (define tree3
56 '(poem (@ (title "The Lovesong of J. Alfred Prufrock")
57 (poet "T. S. Eliot"))
58 (stanza
59 (line "Let us go then, you and I,")
60 (line "When the evening is spread out against the sky")
61 (line "Like a patient etherized upon a table:"))
62 (stanza
63 (line "In the room the women come and go")
64 (line "Talking of Michaelangelo."))))
65
66 (define (run-test selector node expected)
67 (pass-if expected
68 (equal? expected (selector node))))
69
70 (with-test-prefix "test-all"
71
72
73 ;; Location path, full form: child::para
74 ;; Location path, abbreviated form: para
75 ;; selects the para element children of the context node
76 (let ((tree
77 '(elem (@) (para (@) "para") (br (@)) "cdata" (para (@) "second par"))
78 )
79 (expected '((para (@) "para") (para (@) "second par")))
80 )
81 (run-test (select-kids (node-typeof? 'para)) tree expected)
82 (run-test (sxpath '(para)) tree expected))
83
84 ;; Location path, full form: child::*
85 ;; Location path, abbreviated form: *
86 ;; selects all element children of the context node
87 (let ((tree
88 '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
89 )
90 (expected
91 '((para (@) "para") (br (@)) (para "second par")))
92 )
93 (run-test (select-kids (node-typeof? '*)) tree expected)
94 (run-test (sxpath '(*)) tree expected))
95
96 ;; Location path, full form: child::text()
97 ;; Location path, abbreviated form: text()
98 ;; selects all text node children of the context node
99 (let ((tree
100 '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
101 )
102 (expected
103 '("cdata"))
104 )
105 (run-test (select-kids (node-typeof? '*text*)) tree expected)
106 (run-test (sxpath '(*text*)) tree expected))
107
108 ;; Location path, full form: child::node()
109 ;; Location path, abbreviated form: node()
110 ;; selects all the children of the context node, whatever their node type
111 (let* ((tree
112 '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par"))
113 )
114 (expected (cdr tree))
115 )
116 (run-test (select-kids (node-typeof? '*any*)) tree expected)
117 (run-test (sxpath '(*any*)) tree expected)
118 )
119
120 ;; Location path, full form: child::*/child::para
121 ;; Location path, abbreviated form: */para
122 ;; selects all para grandchildren of the context node
123
124 (let ((tree
125 '(elem (@) (para (@) "para") (br (@)) "cdata" (para "second par")
126 (div (@ (name "aa")) (para "third para")))
127 )
128 (expected
129 '((para "third para")))
130 )
131 (run-test
132 (node-join (select-kids (node-typeof? '*))
133 (select-kids (node-typeof? 'para)))
134 tree expected)
135 (run-test (sxpath '(* para)) tree expected)
136 )
137
138
139 ;; Location path, full form: attribute::name
140 ;; Location path, abbreviated form: @name
141 ;; selects the 'name' attribute of the context node
142
143 (let ((tree
144 '(elem (@ (name "elem") (id "idz"))
145 (para (@) "para") (br (@)) "cdata" (para (@) "second par")
146 (div (@ (name "aa")) (para (@) "third para")))
147 )
148 (expected
149 '((name "elem")))
150 )
151 (run-test
152 (node-join (select-kids (node-typeof? '@))
153 (select-kids (node-typeof? 'name)))
154 tree expected)
155 (run-test (sxpath '(@ name)) tree expected)
156 )
157
158 ;; Location path, full form: attribute::*
159 ;; Location path, abbreviated form: @*
160 ;; selects all the attributes of the context node
161 (let ((tree
162 '(elem (@ (name "elem") (id "idz"))
163 (para (@) "para") (br (@)) "cdata" (para "second par")
164 (div (@ (name "aa")) (para (@) "third para")))
165 )
166 (expected
167 '((name "elem") (id "idz")))
168 )
169 (run-test
170 (node-join (select-kids (node-typeof? '@))
171 (select-kids (node-typeof? '*)))
172 tree expected)
173 (run-test (sxpath '(@ *)) tree expected)
174 )
175
176
177 ;; Location path, full form: descendant::para
178 ;; Location path, abbreviated form: .//para
179 ;; selects the para element descendants of the context node
180
181 (let ((tree
182 '(elem (@ (name "elem") (id "idz"))
183 (para (@) "para") (br (@)) "cdata" (para "second par")
184 (div (@ (name "aa")) (para (@) "third para")))
185 )
186 (expected
187 '((para (@) "para") (para "second par") (para (@) "third para")))
188 )
189 (run-test
190 (node-closure (node-typeof? 'para))
191 tree expected)
192 (run-test (sxpath '(// para)) tree expected)
193 )
194
195 ;; Location path, full form: self::para
196 ;; Location path, abbreviated form: _none_
197 ;; selects the context node if it is a para element; otherwise selects nothing
198
199 (let ((tree
200 '(elem (@ (name "elem") (id "idz"))
201 (para (@) "para") (br (@)) "cdata" (para "second par")
202 (div (@ (name "aa")) (para (@) "third para")))
203 )
204 )
205 (run-test (node-self (node-typeof? 'para)) tree '())
206 (run-test (node-self (node-typeof? 'elem)) tree (list tree))
207 )
208
209 ;; Location path, full form: descendant-or-self::node()
210 ;; Location path, abbreviated form: //
211 ;; selects the context node, all the children (including attribute nodes)
212 ;; of the context node, and all the children of all the (element)
213 ;; descendants of the context node.
214 ;; This is _almost_ a powerset of the context node.
215 (let* ((tree
216 '(para (@ (name "elem") (id "idz"))
217 (para (@) "para") (br (@)) "cdata" (para "second par")
218 (div (@ (name "aa")) (para (@) "third para")))
219 )
220 (expected
221 (cons tree
222 (append (cdr tree)
223 '((@) "para" (@) "second par"
224 (@ (name "aa")) (para (@) "third para")
225 (@) "third para"))))
226 )
227 (run-test
228 (node-or
229 (node-self (node-typeof? '*any*))
230 (node-closure (node-typeof? '*any*)))
231 tree expected)
232 (run-test (sxpath '(//)) tree expected)
233 )
234
235 ;; Location path, full form: ancestor::div
236 ;; Location path, abbreviated form: _none_
237 ;; selects all div ancestors of the context node
238 ;; This Location expression is equivalent to the following:
239 ; /descendant-or-self::div[descendant::node() = curr_node]
240 ;; This shows that the ancestor:: axis is actually redundant. Still,
241 ;; it can be emulated as the following SXPath expression demonstrates.
242
243 ;; The insight behind "ancestor::div" -- selecting all "div" ancestors
244 ;; of the current node -- is
245 ;; S[ancestor::div] context_node =
246 ;; { y | y=subnode*(root), context_node=subnode(subnode*(y)),
247 ;; isElement(y), name(y) = "div" }
248 ;; We observe that
249 ;; { y | y=subnode*(root), pred(y) }
250 ;; can be expressed in SXPath as
251 ;; ((node-or (node-self pred) (node-closure pred)) root-node)
252 ;; The composite predicate 'isElement(y) & name(y) = "div"' corresponds to
253 ;; (node-self (node-typeof? 'div)) in SXPath. Finally, filter
254 ;; context_node=subnode(subnode*(y)) is tantamount to
255 ;; (node-closure (node-eq? context-node)), whereas node-reduce denotes the
256 ;; the composition of converters-predicates in the filtering context.
257
258 (let*
259 ((root
260 '(div (@ (name "elem") (id "idz"))
261 (para (@) "para") (br (@)) "cdata" (para (@) "second par")
262 (div (@ (name "aa")) (para (@) "third para"))))
263 (context-node ; /descendant::any()[child::text() == "third para"]
264 (car
265 ((node-closure
266 (select-kids
267 (node-equal? "third para")))
268 root)))
269 (pred
270 (node-reduce (node-self (node-typeof? 'div))
271 (node-closure (node-eq? context-node))
272 ))
273 )
274 (run-test
275 (node-or
276 (node-self pred)
277 (node-closure pred))
278 root
279 (cons root
280 '((div (@ (name "aa")) (para (@) "third para")))))
281 )
282
283
284
285 ;; Location path, full form: child::div/descendant::para
286 ;; Location path, abbreviated form: div//para
287 ;; selects the para element descendants of the div element
288 ;; children of the context node
289
290 (let ((tree
291 '(elem (@ (name "elem") (id "idz"))
292 (para (@) "para") (br (@)) "cdata" (para "second par")
293 (div (@ (name "aa")) (para (@) "third para")
294 (div (para "fourth para"))))
295 )
296 (expected
297 '((para (@) "third para") (para "fourth para")))
298 )
299 (run-test
300 (node-join
301 (select-kids (node-typeof? 'div))
302 (node-closure (node-typeof? 'para)))
303 tree expected)
304 (run-test (sxpath '(div // para)) tree expected)
305 )
306
307
308 ;; Location path, full form: /descendant::olist/child::item
309 ;; Location path, abbreviated form: //olist/item
310 ;; selects all the item elements that have an olist parent (which is not root)
311 ;; and that are in the same document as the context node
312 ;; See the following test.
313
314 ;; Location path, full form: /descendant::td/attribute::align
315 ;; Location path, abbreviated form: //td/@align
316 ;; Selects 'align' attributes of all 'td' elements in tree1
317 (let ((tree tree1)
318 (expected
319 '((align "right") (align "center") (align "center") (align "center"))
320 ))
321 (run-test
322 (node-join
323 (node-closure (node-typeof? 'td))
324 (select-kids (node-typeof? '@))
325 (select-kids (node-typeof? 'align)))
326 tree expected)
327 (run-test (sxpath '(// td @ align)) tree expected)
328 )
329
330
331 ;; Location path, full form: /descendant::td[attribute::align]
332 ;; Location path, abbreviated form: //td[@align]
333 ;; Selects all td elements that have an attribute 'align' in tree1
334 (let ((tree tree1)
335 (expected
336 '((td (@ (align "right")) "Talks ") (td (@ (align "center")) " = ")
337 (td (@ (align "center")) " = ") (td (@ (align "center")) " = "))
338 ))
339 (run-test
340 (node-reduce
341 (node-closure (node-typeof? 'td))
342 (filter
343 (node-join
344 (select-kids (node-typeof? '@))
345 (select-kids (node-typeof? 'align)))))
346 tree expected)
347 (run-test (sxpath `(// td ,(node-self (sxpath '(@ align))))) tree expected)
348 (run-test (sxpath '(// (td (@ align)))) tree expected)
349 (run-test (sxpath '(// ((td) (@ align)))) tree expected)
350 ;; note! (sxpath ...) is a converter. Therefore, it can be used
351 ;; as any other converter, for example, in the full-form SXPath.
352 ;; Thus we can mix the full and abbreviated form SXPath's freely.
353 (run-test
354 (node-reduce
355 (node-closure (node-typeof? 'td))
356 (filter
357 (sxpath '(@ align))))
358 tree expected)
359 )
360
361
362 ;; Location path, full form: /descendant::td[attribute::align = "right"]
363 ;; Location path, abbreviated form: //td[@align = "right"]
364 ;; Selects all td elements that have an attribute align = "right" in tree1
365 (let ((tree tree1)
366 (expected
367 '((td (@ (align "right")) "Talks "))
368 ))
369 (run-test
370 (node-reduce
371 (node-closure (node-typeof? 'td))
372 (filter
373 (node-join
374 (select-kids (node-typeof? '@))
375 (select-kids (node-equal? '(align "right"))))))
376 tree expected)
377 (run-test (sxpath '(// (td (@ (equal? (align "right")))))) tree expected)
378 )
379
380 ;; Location path, full form: child::para[position()=1]
381 ;; Location path, abbreviated form: para[1]
382 ;; selects the first para child of the context node
383 (let ((tree
384 '(elem (@ (name "elem") (id "idz"))
385 (para (@) "para") (br (@)) "cdata" (para "second par")
386 (div (@ (name "aa")) (para (@) "third para")))
387 )
388 (expected
389 '((para (@) "para"))
390 ))
391 (run-test
392 (node-reduce
393 (select-kids (node-typeof? 'para))
394 (node-pos 1))
395 tree expected)
396 (run-test (sxpath '((para 1))) tree expected)
397 )
398
399 ;; Location path, full form: child::para[position()=last()]
400 ;; Location path, abbreviated form: para[last()]
401 ;; selects the last para child of the context node
402 (let ((tree
403 '(elem (@ (name "elem") (id "idz"))
404 (para (@) "para") (br (@)) "cdata" (para "second par")
405 (div (@ (name "aa")) (para (@) "third para")))
406 )
407 (expected
408 '((para "second par"))
409 ))
410 (run-test
411 (node-reduce
412 (select-kids (node-typeof? 'para))
413 (node-pos -1))
414 tree expected)
415 (run-test (sxpath '((para -1))) tree expected)
416 )
417
418 ;; Illustrating the following Note of Sec 2.5 of XPath:
419 ;; "NOTE: The location path //para[1] does not mean the same as the
420 ;; location path /descendant::para[1]. The latter selects the first
421 ;; descendant para element; the former selects all descendant para
422 ;; elements that are the first para children of their parents."
423
424 (let ((tree
425 '(elem (@ (name "elem") (id "idz"))
426 (para (@) "para") (br (@)) "cdata" (para "second par")
427 (div (@ (name "aa")) (para (@) "third para")))
428 )
429 )
430 (run-test
431 (node-reduce ; /descendant::para[1] in SXPath
432 (node-closure (node-typeof? 'para))
433 (node-pos 1))
434 tree '((para (@) "para")))
435 (run-test (sxpath '(// (para 1))) tree
436 '((para (@) "para") (para (@) "third para")))
437 )
438
439 ;; Location path, full form: parent::node()
440 ;; Location path, abbreviated form: ..
441 ;; selects the parent of the context node. The context node may be
442 ;; an attribute node!
443 ;; For the last test:
444 ;; Location path, full form: parent::*/attribute::name
445 ;; Location path, abbreviated form: ../@name
446 ;; Selects the name attribute of the parent of the context node
447
448 (let* ((tree
449 '(elem (@ (name "elem") (id "idz"))
450 (para (@) "para") (br (@)) "cdata" (para "second par")
451 (div (@ (name "aa")) (para (@) "third para")))
452 )
453 (para1 ; the first para node
454 (car ((sxpath '(para)) tree)))
455 (para3 ; the third para node
456 (car ((sxpath '(div para)) tree)))
457 (div ; div node
458 (car ((sxpath '(// div)) tree)))
459 )
460 (run-test
461 (node-parent tree)
462 para1 (list tree))
463 (run-test
464 (node-parent tree)
465 para3 (list div))
466 (run-test ; checking the parent of an attribute node
467 (node-parent tree)
468 ((sxpath '(@ name)) div) (list div))
469 (run-test
470 (node-join
471 (node-parent tree)
472 (select-kids (node-typeof? '@))
473 (select-kids (node-typeof? 'name)))
474 para3 '((name "aa")))
475 (run-test
476 (sxpath `(,(node-parent tree) @ name))
477 para3 '((name "aa")))
478 )
479
480 ;; Location path, full form: following-sibling::chapter[position()=1]
481 ;; Location path, abbreviated form: none
482 ;; selects the next chapter sibling of the context node
483 ;; The path is equivalent to
484 ;; let cnode = context-node
485 ;; in
486 ;; parent::* / child::chapter [take-after node_eq(self::*,cnode)]
487 ;; [position()=1]
488 (let* ((tree
489 '(document
490 (preface "preface")
491 (chapter (@ (id "one")) "Chap 1 text")
492 (chapter (@ (id "two")) "Chap 2 text")
493 (chapter (@ (id "three")) "Chap 3 text")
494 (chapter (@ (id "four")) "Chap 4 text")
495 (epilogue "Epilogue text")
496 (appendix (@ (id "A")) "App A text")
497 (References "References"))
498 )
499 (a-node ; to be used as a context node
500 (car ((sxpath '(// (chapter (@ (equal? (id "two")))))) tree)))
501 (expected
502 '((chapter (@ (id "three")) "Chap 3 text")))
503 )
504 (run-test
505 (node-reduce
506 (node-join
507 (node-parent tree)
508 (select-kids (node-typeof? 'chapter)))
509 (take-after (node-eq? a-node))
510 (node-pos 1)
511 )
512 a-node expected)
513 )
514
515 ;; preceding-sibling::chapter[position()=1]
516 ;; selects the previous chapter sibling of the context node
517 ;; The path is equivalent to
518 ;; let cnode = context-node
519 ;; in
520 ;; parent::* / child::chapter [take-until node_eq(self::*,cnode)]
521 ;; [position()=-1]
522 (let* ((tree
523 '(document
524 (preface "preface")
525 (chapter (@ (id "one")) "Chap 1 text")
526 (chapter (@ (id "two")) "Chap 2 text")
527 (chapter (@ (id "three")) "Chap 3 text")
528 (chapter (@ (id "four")) "Chap 4 text")
529 (epilogue "Epilogue text")
530 (appendix (@ (id "A")) "App A text")
531 (References "References"))
532 )
533 (a-node ; to be used as a context node
534 (car ((sxpath '(// (chapter (@ (equal? (id "three")))))) tree)))
535 (expected
536 '((chapter (@ (id "two")) "Chap 2 text")))
537 )
538 (run-test
539 (node-reduce
540 (node-join
541 (node-parent tree)
542 (select-kids (node-typeof? 'chapter)))
543 (take-until (node-eq? a-node))
544 (node-pos -1)
545 )
546 a-node expected)
547 )
548
549
550 ;; /descendant::figure[position()=42]
551 ;; selects the forty-second figure element in the document
552 ;; See the next example, which is more general.
553
554 ;; Location path, full form:
555 ;; child::table/child::tr[position()=2]/child::td[position()=3]
556 ;; Location path, abbreviated form: table/tr[2]/td[3]
557 ;; selects the third td of the second tr of the table
558 (let ((tree ((node-closure (node-typeof? 'p)) tree1))
559 (expected
560 '((td " data + control"))
561 ))
562 (run-test
563 (node-join
564 (select-kids (node-typeof? 'table))
565 (node-reduce (select-kids (node-typeof? 'tr))
566 (node-pos 2))
567 (node-reduce (select-kids (node-typeof? 'td))
568 (node-pos 3)))
569 tree expected)
570 (run-test (sxpath '(table (tr 2) (td 3))) tree expected)
571 )
572
573
574 ;; Location path, full form:
575 ;; child::para[attribute::type='warning'][position()=5]
576 ;; Location path, abbreviated form: para[@type='warning'][5]
577 ;; selects the fifth para child of the context node that has a type
578 ;; attribute with value warning
579 (let ((tree
580 '(chapter
581 (para "para1")
582 (para (@ (type "warning")) "para 2")
583 (para (@ (type "warning")) "para 3")
584 (para (@ (type "warning")) "para 4")
585 (para (@ (type "warning")) "para 5")
586 (para (@ (type "warning")) "para 6"))
587 )
588 (expected
589 '((para (@ (type "warning")) "para 6"))
590 ))
591 (run-test
592 (node-reduce
593 (select-kids (node-typeof? 'para))
594 (filter
595 (node-join
596 (select-kids (node-typeof? '@))
597 (select-kids (node-equal? '(type "warning")))))
598 (node-pos 5))
599 tree expected)
600 (run-test (sxpath '( (((para (@ (equal? (type "warning"))))) 5 ) ))
601 tree expected)
602 (run-test (sxpath '( (para (@ (equal? (type "warning"))) 5 ) ))
603 tree expected)
604 )
605
606
607 ;; Location path, full form:
608 ;; child::para[position()=5][attribute::type='warning']
609 ;; Location path, abbreviated form: para[5][@type='warning']
610 ;; selects the fifth para child of the context node if that child has a 'type'
611 ;; attribute with value warning
612 (let ((tree
613 '(chapter
614 (para "para1")
615 (para (@ (type "warning")) "para 2")
616 (para (@ (type "warning")) "para 3")
617 (para (@ (type "warning")) "para 4")
618 (para (@ (type "warning")) "para 5")
619 (para (@ (type "warning")) "para 6"))
620 )
621 (expected
622 '((para (@ (type "warning")) "para 5"))
623 ))
624 (run-test
625 (node-reduce
626 (select-kids (node-typeof? 'para))
627 (node-pos 5)
628 (filter
629 (node-join
630 (select-kids (node-typeof? '@))
631 (select-kids (node-equal? '(type "warning"))))))
632 tree expected)
633 (run-test (sxpath '( (( (para 5)) (@ (equal? (type "warning"))))))
634 tree expected)
635 (run-test (sxpath '( (para 5 (@ (equal? (type "warning")))) ))
636 tree expected)
637 )
638
639 ;; Location path, full form:
640 ;; child::*[self::chapter or self::appendix]
641 ;; Location path, semi-abbreviated form: *[self::chapter or self::appendix]
642 ;; selects the chapter and appendix children of the context node
643 (let ((tree
644 '(document
645 (preface "preface")
646 (chapter (@ (id "one")) "Chap 1 text")
647 (chapter (@ (id "two")) "Chap 2 text")
648 (chapter (@ (id "three")) "Chap 3 text")
649 (epilogue "Epilogue text")
650 (appendix (@ (id "A")) "App A text")
651 (References "References"))
652 )
653 (expected
654 '((chapter (@ (id "one")) "Chap 1 text")
655 (chapter (@ (id "two")) "Chap 2 text")
656 (chapter (@ (id "three")) "Chap 3 text")
657 (appendix (@ (id "A")) "App A text"))
658 ))
659 (run-test
660 (node-join
661 (select-kids (node-typeof? '*))
662 (filter
663 (node-or
664 (node-self (node-typeof? 'chapter))
665 (node-self (node-typeof? 'appendix)))))
666 tree expected)
667 (run-test (sxpath `(* ,(node-or (node-self (node-typeof? 'chapter))
668 (node-self (node-typeof? 'appendix)))))
669 tree expected)
670 )
671
672
673 ;; Location path, full form: child::chapter[child::title='Introduction']
674 ;; Location path, abbreviated form: chapter[title = 'Introduction']
675 ;; selects the chapter children of the context node that have one or more
676 ;; title children with string-value equal to Introduction
677 ;; See a similar example: //td[@align = "right"] above.
678
679 ;; Location path, full form: child::chapter[child::title]
680 ;; Location path, abbreviated form: chapter[title]
681 ;; selects the chapter children of the context node that have one or
682 ;; more title children
683 ;; See a similar example //td[@align] above.
684
685 (let ((tree tree3)
686 (expected
687 '("Let us go then, you and I," "In the room the women come and go")
688 ))
689 (run-test
690 (node-join
691 (node-closure (node-typeof? 'stanza))
692 (node-reduce
693 (select-kids (node-typeof? 'line)) (node-pos 1))
694 (select-kids (node-typeof? '*text*)))
695 tree expected)
696 (run-test (sxpath '(// stanza (line 1) *text*)) tree expected)
697 )
698 )