Commit | Line | Data |
---|---|---|
de9df04a AW |
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 | |
500f6a47 AW |
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 | ) |