2 # -*- coding: utf-8 -*-
5 # Author: David Goodger <goodger@python.org>
6 # Copyright: This module has been placed in the public domain.
9 Test module for nodes.py.
15 import DocutilsTestSupport
# must be imported before docutils
16 from DocutilsTestSupport
import nodes
, utils
20 if sys
.version_info
>= (3, 0):
24 class TextTests(unittest
.TestCase
):
27 self
.text
= nodes
.Text('Line 1.\nLine 2.')
28 self
.unicode_text
= nodes
.Text(u
'Möhren')
29 self
.longtext
= nodes
.Text('Mary had a little lamb whose '
30 'fleece was white as snow and '
31 'everwhere that Mary went the '
32 'lamb was sure to go.')
35 self
.assertEqual(repr(self
.text
), r
"<#text: 'Line 1.\nLine 2.'>")
36 self
.assertEqual(self
.text
.shortrepr(),
37 r
"<#text: 'Line 1.\nLine 2.'>")
38 self
.assertEqual(nodes
.reprunicode('foo'), u
'foo')
39 if sys
.version_info
< (3, 0):
40 self
.assertEqual(repr(self
.unicode_text
), r
"<#text: 'M\xf6hren'>")
42 self
.assertEqual(repr(self
.unicode_text
), u
"<#text: 'Möhren'>")
45 self
.assertEqual(str(self
.text
), 'Line 1.\nLine 2.')
47 def test_unicode(self
):
48 self
.assertEqual(unicode(self
.unicode_text
), u
'Möhren')
49 self
.assertEqual(str(self
.unicode_text
), 'M\xf6hren')
51 def test_astext(self
):
52 self
.assertTrue(isinstance(self
.text
.astext(), unicode))
53 self
.assertEqual(self
.text
.astext(), u
'Line 1.\nLine 2.')
54 self
.assertEqual(self
.unicode_text
.astext(), u
'Möhren')
56 def test_pformat(self
):
57 self
.assertTrue(isinstance(self
.text
.pformat(), unicode))
58 self
.assertEqual(self
.text
.pformat(), u
'Line 1.\nLine 2.\n')
61 text
= nodes
.Text(' was noch ', r
' \was\ noch \\ ')
62 stripped
= text
.lstrip().rstrip()
63 stripped2
= text
.lstrip(' wahn').rstrip(' wahn')
64 self
.assertEqual(stripped
, u
'was noch')
65 self
.assertEqual(stripped2
, u
's noc')
67 def test_asciirestriction(self
):
68 if sys
.version_info
< (3, 0):
69 self
.assertRaises(UnicodeDecodeError, nodes
.Text
,
72 # no bytes at all allowed
73 self
.assertRaises(TypeError, nodes
.Text
, b
'hol')
75 def test_longrepr(self
):
76 self
.assertEqual(repr(self
.longtext
), r
"<#text: 'Mary had a "
77 r
"little lamb whose fleece was white as snow "
79 self
.assertEqual(self
.longtext
.shortrepr(),
80 r
"<#text: 'Mary had a lit ...'>")
82 class ElementTests(unittest
.TestCase
):
85 element
= nodes
.Element()
86 self
.assertEqual(repr(element
), '<Element: >')
87 self
.assertEqual(str(element
), '<Element/>')
89 self
.assertEqual(dom
.toxml(), '<Element/>')
92 self
.assertEqual(repr(element
), '<Element: >')
93 self
.assertEqual(str(element
), '<Element attr="1"/>')
95 self
.assertEqual(dom
.toxml(), '<Element attr="1"/>')
97 self
.assertEqual(element
.pformat(), '<Element attr="1">\n')
99 element
['mark'] = u
'\u2022'
100 self
.assertEqual(repr(element
), '<Element: >')
101 if sys
.version_info
< (3, 0):
102 self
.assertEqual(str(element
), '<Element mark="\\u2022"/>')
104 self
.assertEqual(str(element
), u
'<Element mark="\u2022"/>')
105 dom
= element
.asdom()
106 self
.assertEqual(dom
.toxml(), u
'<Element mark="\u2022"/>')
108 element
['names'] = ['nobody', u
'имя', u
'näs']
109 if sys
.version_info
< (3, 0):
110 self
.assertEqual(repr(element
),
111 '<Element "nobody; \\u0438\\u043c\\u044f; n\\xe4s": >')
113 self
.assertEqual(repr(element
), u
'<Element "nobody; имя; näs": >')
114 self
.assertTrue(isinstance(repr(element
), str))
116 def test_withtext(self
):
117 element
= nodes
.Element('text\nmore', nodes
.Text('text\nmore'))
118 uelement
= nodes
.Element(u
'grün', nodes
.Text(u
'grün'))
119 self
.assertEqual(repr(element
), r
"<Element: <#text: 'text\nmore'>>")
120 if sys
.version_info
< (3, 0):
121 self
.assertEqual(repr(uelement
), "<Element: <#text: 'gr\\xfcn'>>")
123 self
.assertEqual(repr(uelement
), u
"<Element: <#text: 'grün'>>")
124 self
.assertTrue(isinstance(repr(uelement
), str))
125 self
.assertEqual(str(element
), '<Element>text\nmore</Element>')
126 self
.assertEqual(str(uelement
), '<Element>gr\xfcn</Element>')
127 dom
= element
.asdom()
128 self
.assertEqual(dom
.toxml(), '<Element>text\nmore</Element>')
130 element
['attr'] = '1'
131 self
.assertEqual(repr(element
), r
"<Element: <#text: 'text\nmore'>>")
132 self
.assertEqual(str(element
),
133 '<Element attr="1">text\nmore</Element>')
134 dom
= element
.asdom()
135 self
.assertEqual(dom
.toxml(),
136 '<Element attr="1">text\nmore</Element>')
138 self
.assertEqual(element
.pformat(),
139 '<Element attr="1">\n text\n more\n')
141 def test_clear(self
):
142 element
= nodes
.Element()
143 element
+= nodes
.Element()
144 self
.assertTrue(len(element
))
146 self
.assertTrue(not len(element
))
148 def test_normal_attributes(self
):
149 element
= nodes
.Element()
150 self
.assertTrue('foo' not in element
)
151 self
.assertRaises(KeyError, element
.__getitem
__, 'foo')
152 element
['foo'] = 'sometext'
153 self
.assertEqual(element
['foo'], 'sometext')
155 self
.assertRaises(KeyError, element
.__getitem
__, 'foo')
157 def test_default_attributes(self
):
158 element
= nodes
.Element()
159 self
.assertEqual(element
['ids'], [])
160 self
.assertEqual(element
.non_default_attributes(), {})
161 self
.assertTrue(not element
.is_not_default('ids'))
162 self
.assertTrue(element
['ids'] is not nodes
.Element()['ids'])
163 element
['ids'].append('someid')
164 self
.assertEqual(element
['ids'], ['someid'])
165 self
.assertEqual(element
.non_default_attributes(),
167 self
.assertTrue(element
.is_not_default('ids'))
169 def test_update_basic_atts(self
):
170 element1
= nodes
.Element(ids
=['foo', 'bar'], test
=['test1'])
171 element2
= nodes
.Element(ids
=['baz', 'qux'], test
=['test2'])
172 element1
.update_basic_atts(element2
)
173 # 'ids' are appended because 'ids' is a basic attribute.
174 self
.assertEqual(element1
['ids'], ['foo', 'bar', 'baz', 'qux'])
175 # 'test' is not overwritten because it is not a basic attribute.
176 self
.assertEqual(element1
['test'], ['test1'])
178 def test_update_all_atts(self
):
179 # Note: Also tests is_not_list_attribute and is_not_known_attribute
180 # and various helpers
181 ## Test for full attribute replacement
182 element1
= nodes
.Element(ids
=['foo', 'bar'], parent_only
='parent',
184 element2
= nodes
.Element(ids
=['baz', 'qux'], child_only
='child',
185 all_nodes
='dad', source
='source')
187 # Test for when same fields are replaced as well as source...
188 element1
.update_all_atts_consistantly(element2
, True, True)
189 # 'ids' are appended because 'ids' is a basic attribute.
190 self
.assertEqual(element1
['ids'], ['foo', 'bar', 'baz', 'qux'])
191 # 'parent_only' should remain unaffected.
192 self
.assertEqual(element1
['parent_only'], 'parent')
193 # 'all_nodes' is overwritten due to the second parameter == True.
194 self
.assertEqual(element1
['all_nodes'], 'dad')
195 # 'child_only' should have been added.
196 self
.assertEqual(element1
['child_only'], 'child')
197 # 'source' is also overwritten due to the third parameter == True.
198 self
.assertEqual(element1
['source'], 'source')
200 # Test for when same fields are replaced but not source...
201 element1
= nodes
.Element(ids
=['foo', 'bar'], parent_only
='parent',
203 element1
.update_all_atts_consistantly(element2
)
204 # 'ids' are appended because 'ids' is a basic attribute.
205 self
.assertEqual(element1
['ids'], ['foo', 'bar', 'baz', 'qux'])
206 # 'parent_only' should remain unaffected.
207 self
.assertEqual(element1
['parent_only'], 'parent')
208 # 'all_nodes' is overwritten due to the second parameter default of True.
209 self
.assertEqual(element1
['all_nodes'], 'dad')
210 # 'child_only' should have been added.
211 self
.assertEqual(element1
['child_only'], 'child')
212 # 'source' remains unset due to the third parameter default of False.
213 self
.assertEqual(element1
.get('source'), None)
215 # Test for when fields are NOT replaced but source is...
216 element1
= nodes
.Element(ids
=['foo', 'bar'], parent_only
='parent',
218 element1
.update_all_atts_consistantly(element2
, False, True)
219 # 'ids' are appended because 'ids' is a basic attribute.
220 self
.assertEqual(element1
['ids'], ['foo', 'bar', 'baz', 'qux'])
221 # 'parent_only' should remain unaffected.
222 self
.assertEqual(element1
['parent_only'], 'parent')
223 # 'all_nodes' is preserved due to the second parameter == False.
224 self
.assertEqual(element1
['all_nodes'], 'mom')
225 # 'child_only' should have been added.
226 self
.assertEqual(element1
['child_only'], 'child')
227 # 'source' is added due to the third parameter == True.
228 self
.assertEqual(element1
['source'], 'source')
229 element1
= nodes
.Element(source
='destination')
230 element1
.update_all_atts_consistantly(element2
, False, True)
231 # 'source' remains unchanged due to the second parameter == False.
232 self
.assertEqual(element1
['source'], 'destination')
234 # Test for when same fields are replaced but not source...
235 element1
= nodes
.Element(ids
=['foo', 'bar'], parent_only
='parent',
237 element1
.update_all_atts_consistantly(element2
, False)
238 # 'ids' are appended because 'ids' is a basic attribute.
239 self
.assertEqual(element1
['ids'], ['foo', 'bar', 'baz', 'qux'])
240 # 'parent_only' should remain unaffected.
241 self
.assertEqual(element1
['parent_only'], 'parent')
242 # 'all_nodes' is preserved due to the second parameter == False.
243 self
.assertEqual(element1
['all_nodes'], 'mom')
244 # 'child_only' should have been added.
245 self
.assertEqual(element1
['child_only'], 'child')
246 # 'source' remains unset due to the third parameter default of False.
247 self
.assertEqual(element1
.get('source'), None)
249 ## Test for List attribute merging
250 # Attribute Concatination
251 element1
= nodes
.Element(ss
='a', sl
='1', ls
=['I'], ll
=['A'])
252 element2
= nodes
.Element(ss
='b', sl
=['2'], ls
='II', ll
=['B'])
253 element1
.update_all_atts_concatenating(element2
)
254 # 'ss' is replaced because non-list
255 self
.assertEqual(element1
['ss'], 'b')
256 # 'sl' is replaced because they are both not lists
257 self
.assertEqual(element1
['sl'], ['2'])
258 # 'ls' is replaced because they are both not lists
259 self
.assertEqual(element1
['ls'], 'II')
260 # 'll' is extended because they are both lists
261 self
.assertEqual(element1
['ll'], ['A', 'B'])
264 element1
= nodes
.Element(ss
='a', sl
='1', ls
=['I'], ll
=['A'])
265 element2
= nodes
.Element(ss
='b', sl
=['2'], ls
='II', ll
=['B'])
266 element1
.update_all_atts_coercion(element2
)
267 # 'ss' is replaced because non-list
268 self
.assertEqual(element1
['ss'], 'b')
269 # 'sl' is converted to a list and appended because element2 has a list
270 self
.assertEqual(element1
['sl'], ['1', '2'])
271 # 'ls' has element2's value appended to the list
272 self
.assertEqual(element1
['ls'], ['I', 'II'])
273 # 'll' is extended because they are both lists
274 self
.assertEqual(element1
['ll'], ['A', 'B'])
276 # Attribute Conversion
277 element1
= nodes
.Element(ss
='a', sl
='1', ls
=['I'], ll
=['A'])
278 element2
= nodes
.Element(ss
='b', sl
=['2'], ls
='II', ll
=['B'])
279 element1
.update_all_atts_convert(element2
)
280 # 'ss' is converted to a list with the values from each element
281 self
.assertEqual(element1
['ss'], ['a', 'b'])
282 # 'sl' is converted to a list and appended
283 self
.assertEqual(element1
['sl'], ['1', '2'])
284 # 'ls' has element2's value appended to the list
285 self
.assertEqual(element1
['ls'], ['I', 'II'])
287 self
.assertEqual(element1
['ll'], ['A', 'B'])
289 def test_replace_self(self
):
290 parent
= nodes
.Element(ids
=['parent'])
291 child1
= nodes
.Element(ids
=['child1'])
292 grandchild
= nodes
.Element(ids
=['grandchild'])
294 child2
= nodes
.Element(ids
=['child2'])
295 twins
= [nodes
.Element(ids
=['twin%s' % i
]) for i
in (1, 2)]
297 child3
= nodes
.Element(ids
=['child3'])
298 child4
= nodes
.Element(ids
=['child4'])
299 parent
+= [child1
, child2
, child3
, child4
]
300 self
.assertEqual(parent
.pformat(), """\
301 <Element ids="parent">
302 <Element ids="child1">
303 <Element ids="grandchild">
304 <Element ids="child2">
305 <Element ids="twin1">
306 <Element ids="twin2">
307 <Element ids="child3">
308 <Element ids="child4">
310 # Replace child1 with the grandchild.
311 child1
.replace_self(child1
[0])
312 self
.assertEqual(parent
[0], grandchild
)
313 # Assert that 'ids' have been updated.
314 self
.assertEqual(grandchild
['ids'], ['grandchild', 'child1'])
315 # Replace child2 with its children.
316 child2
.replace_self(child2
[:])
317 self
.assertEqual(parent
[1:3], twins
)
318 # Assert that 'ids' have been propagated to first child.
319 self
.assertEqual(twins
[0]['ids'], ['twin1', 'child2'])
320 self
.assertEqual(twins
[1]['ids'], ['twin2'])
321 # Replace child3 with new child.
322 newchild
= nodes
.Element(ids
=['newchild'])
323 child3
.replace_self(newchild
)
324 self
.assertEqual(parent
[3], newchild
)
325 self
.assertEqual(newchild
['ids'], ['newchild', 'child3'])
326 # Crazy but possible case: Substitute child4 for itself.
327 child4
.replace_self(child4
)
328 # Make sure the 'child4' ID hasn't been duplicated.
329 self
.assertEqual(child4
['ids'], ['child4'])
330 self
.assertEqual(len(parent
), 5)
332 def test_unicode(self
):
333 node
= nodes
.Element(u
'Möhren', nodes
.Text(u
'Möhren', u
'Möhren'))
334 self
.assertEqual(unicode(node
), u
'<Element>Möhren</Element>')
337 class MiscTests(unittest
.TestCase
):
339 def test_reprunicode(self
):
340 # return `unicode` instance
341 self
.assertTrue(isinstance(nodes
.reprunicode('foo'), unicode))
342 self
.assertEqual(nodes
.reprunicode('foo'), u
'foo')
343 self
.assertEqual(nodes
.reprunicode(u
'Möhre'), u
'Möhre')
344 if sys
.version_info
< (3, 0): # strip leading "u" from representation
345 self
.assertEqual(repr(nodes
.reprunicode(u
'Möhre')),
347 else: # no change to `unicode` under Python 3k
348 self
.assertEqual(repr(nodes
.reprunicode(u
'Möhre')), repr(u
'Möhre'))
350 def test_ensure_str(self
):
351 self
.assertTrue(isinstance(nodes
.ensure_str(u
'über'), str))
352 self
.assertEqual(nodes
.ensure_str('over'), 'over')
353 if sys
.version_info
< (3, 0): # strip leading "u" from representation
354 self
.assertEqual(nodes
.ensure_str(u
'über'), r
'\xfcber')
356 self
.assertEqual(nodes
.ensure_str(u
'über'), r
'über')
358 def test_node_class_names(self
):
359 node_class_names
= []
361 c
= getattr(nodes
, x
)
362 if isinstance(c
, type) and \
363 issubclass(c
, nodes
.Node
) and len(c
.__bases
__) > 1:
364 node_class_names
.append(x
)
365 node_class_names
.sort()
366 nodes
.node_class_names
.sort()
367 self
.assertEqual(node_class_names
, nodes
.node_class_names
)
369 ids
= [(u
'a', 'a'), ('A', 'a'), ('', ''), ('a b \n c', 'a-b-c'),
370 ('a.b.c', 'a-b-c'), (' - a - b - c - ', 'a-b-c'), (' - ', ''),
371 (u
'\u2020\u2066', ''), (u
'a \xa7 b \u2020 c', 'a-b-c'),
372 ('1', ''), ('1abc', 'abc'),
375 (u
'\u00f8 o with stroke', 'o-o-with-stroke'),
376 (u
'\u0111 d with stroke', 'd-d-with-stroke'),
377 (u
'\u0127 h with stroke', 'h-h-with-stroke'),
378 (u
'\u0131 dotless i', 'i-dotless-i'),
379 (u
'\u0142 l with stroke', 'l-l-with-stroke'),
380 (u
'\u0167 t with stroke', 't-t-with-stroke'),
381 # From Latin Extended-B
382 (u
'\u0180 b with stroke', 'b-b-with-stroke'),
383 (u
'\u0183 b with topbar', 'b-b-with-topbar'),
384 (u
'\u0188 c with hook', 'c-c-with-hook'),
385 (u
'\u018c d with topbar', 'd-d-with-topbar'),
386 (u
'\u0192 f with hook', 'f-f-with-hook'),
387 (u
'\u0199 k with hook', 'k-k-with-hook'),
388 (u
'\u019a l with bar', 'l-l-with-bar'),
389 (u
'\u019e n with long right leg', 'n-n-with-long-right-leg'),
390 (u
'\u01a5 p with hook', 'p-p-with-hook'),
391 (u
'\u01ab t with palatal hook', 't-t-with-palatal-hook'),
392 (u
'\u01ad t with hook', 't-t-with-hook'),
393 (u
'\u01b4 y with hook', 'y-y-with-hook'),
394 (u
'\u01b6 z with stroke', 'z-z-with-stroke'),
395 (u
'\u01e5 g with stroke', 'g-g-with-stroke'),
396 (u
'\u0225 z with hook', 'z-z-with-hook'),
397 (u
'\u0234 l with curl', 'l-l-with-curl'),
398 (u
'\u0235 n with curl', 'n-n-with-curl'),
399 (u
'\u0236 t with curl', 't-t-with-curl'),
400 (u
'\u0237 dotless j', 'j-dotless-j'),
401 (u
'\u023c c with stroke', 'c-c-with-stroke'),
402 (u
'\u023f s with swash tail', 's-s-with-swash-tail'),
403 (u
'\u0240 z with swash tail', 'z-z-with-swash-tail'),
404 (u
'\u0247 e with stroke', 'e-e-with-stroke'),
405 (u
'\u0249 j with stroke', 'j-j-with-stroke'),
406 (u
'\u024b q with hook tail', 'q-q-with-hook-tail'),
407 (u
'\u024d r with stroke', 'r-r-with-stroke'),
408 (u
'\u024f y with stroke', 'y-y-with-stroke'),
409 # From Latin-1 Supplements
410 (u
'\u00e0: a with grave', 'a-a-with-grave'),
411 (u
'\u00e1 a with acute', 'a-a-with-acute'),
412 (u
'\u00e2 a with circumflex', 'a-a-with-circumflex'),
413 (u
'\u00e3 a with tilde', 'a-a-with-tilde'),
414 (u
'\u00e4 a with diaeresis', 'a-a-with-diaeresis'),
415 (u
'\u00e5 a with ring above', 'a-a-with-ring-above'),
416 (u
'\u00e7 c with cedilla', 'c-c-with-cedilla'),
417 (u
'\u00e8 e with grave', 'e-e-with-grave'),
418 (u
'\u00e9 e with acute', 'e-e-with-acute'),
419 (u
'\u00ea e with circumflex', 'e-e-with-circumflex'),
420 (u
'\u00eb e with diaeresis', 'e-e-with-diaeresis'),
421 (u
'\u00ec i with grave', 'i-i-with-grave'),
422 (u
'\u00ed i with acute', 'i-i-with-acute'),
423 (u
'\u00ee i with circumflex', 'i-i-with-circumflex'),
424 (u
'\u00ef i with diaeresis', 'i-i-with-diaeresis'),
425 (u
'\u00f1 n with tilde', 'n-n-with-tilde'),
426 (u
'\u00f2 o with grave', 'o-o-with-grave'),
427 (u
'\u00f3 o with acute', 'o-o-with-acute'),
428 (u
'\u00f4 o with circumflex', 'o-o-with-circumflex'),
429 (u
'\u00f5 o with tilde', 'o-o-with-tilde'),
430 (u
'\u00f6 o with diaeresis', 'o-o-with-diaeresis'),
431 (u
'\u00f9 u with grave', 'u-u-with-grave'),
432 (u
'\u00fa u with acute', 'u-u-with-acute'),
433 (u
'\u00fb u with circumflex', 'u-u-with-circumflex'),
434 (u
'\u00fc u with diaeresis', 'u-u-with-diaeresis'),
435 (u
'\u00fd y with acute', 'y-y-with-acute'),
436 (u
'\u00ff y with diaeresis', 'y-y-with-diaeresis'),
437 # From Latin Extended-A
438 (u
'\u0101 a with macron', 'a-a-with-macron'),
439 (u
'\u0103 a with breve', 'a-a-with-breve'),
440 (u
'\u0105 a with ogonek', 'a-a-with-ogonek'),
441 (u
'\u0107 c with acute', 'c-c-with-acute'),
442 (u
'\u0109 c with circumflex', 'c-c-with-circumflex'),
443 (u
'\u010b c with dot above', 'c-c-with-dot-above'),
444 (u
'\u010d c with caron', 'c-c-with-caron'),
445 (u
'\u010f d with caron', 'd-d-with-caron'),
446 (u
'\u0113 e with macron', 'e-e-with-macron'),
447 (u
'\u0115 e with breve', 'e-e-with-breve'),
448 (u
'\u0117 e with dot above', 'e-e-with-dot-above'),
449 (u
'\u0119 e with ogonek', 'e-e-with-ogonek'),
450 (u
'\u011b e with caron', 'e-e-with-caron'),
451 (u
'\u011d g with circumflex', 'g-g-with-circumflex'),
452 (u
'\u011f g with breve', 'g-g-with-breve'),
453 (u
'\u0121 g with dot above', 'g-g-with-dot-above'),
454 (u
'\u0123 g with cedilla', 'g-g-with-cedilla'),
455 (u
'\u0125 h with circumflex', 'h-h-with-circumflex'),
456 (u
'\u0129 i with tilde', 'i-i-with-tilde'),
457 (u
'\u012b i with macron', 'i-i-with-macron'),
458 (u
'\u012d i with breve', 'i-i-with-breve'),
459 (u
'\u012f i with ogonek', 'i-i-with-ogonek'),
460 (u
'\u0133 ligature ij', 'ij-ligature-ij'),
461 (u
'\u0135 j with circumflex', 'j-j-with-circumflex'),
462 (u
'\u0137 k with cedilla', 'k-k-with-cedilla'),
463 (u
'\u013a l with acute', 'l-l-with-acute'),
464 (u
'\u013c l with cedilla', 'l-l-with-cedilla'),
465 (u
'\u013e l with caron', 'l-l-with-caron'),
466 (u
'\u0140 l with middle dot', 'l-l-with-middle-dot'),
467 (u
'\u0144 n with acute', 'n-n-with-acute'),
468 (u
'\u0146 n with cedilla', 'n-n-with-cedilla'),
469 (u
'\u0148 n with caron', 'n-n-with-caron'),
470 (u
'\u014d o with macron', 'o-o-with-macron'),
471 (u
'\u014f o with breve', 'o-o-with-breve'),
472 (u
'\u0151 o with double acute', 'o-o-with-double-acute'),
473 (u
'\u0155 r with acute', 'r-r-with-acute'),
474 (u
'\u0157 r with cedilla', 'r-r-with-cedilla'),
475 (u
'\u0159 r with caron', 'r-r-with-caron'),
476 (u
'\u015b s with acute', 's-s-with-acute'),
477 (u
'\u015d s with circumflex', 's-s-with-circumflex'),
478 (u
'\u015f s with cedilla', 's-s-with-cedilla'),
479 (u
'\u0161 s with caron', 's-s-with-caron'),
480 (u
'\u0163 t with cedilla', 't-t-with-cedilla'),
481 (u
'\u0165 t with caron', 't-t-with-caron'),
482 (u
'\u0169 u with tilde', 'u-u-with-tilde'),
483 (u
'\u016b u with macron', 'u-u-with-macron'),
484 (u
'\u016d u with breve', 'u-u-with-breve'),
485 (u
'\u016f u with ring above', 'u-u-with-ring-above'),
486 (u
'\u0171 u with double acute', 'u-u-with-double-acute'),
487 (u
'\u0173 u with ogonek', 'u-u-with-ogonek'),
488 (u
'\u0175 w with circumflex', 'w-w-with-circumflex'),
489 (u
'\u0177 y with circumflex', 'y-y-with-circumflex'),
490 (u
'\u017a z with acute', 'z-z-with-acute'),
491 (u
'\u017c z with dot above', 'z-z-with-dot-above'),
492 (u
'\u017e z with caron', 'z-z-with-caron'),
493 # From Latin Extended-B
494 (u
'\u01a1 o with horn', 'o-o-with-horn'),
495 (u
'\u01b0 u with horn', 'u-u-with-horn'),
496 (u
'\u01c6 dz with caron', 'dz-dz-with-caron'),
497 (u
'\u01c9 lj', 'lj-lj'),
498 (u
'\u01cc nj', 'nj-nj'),
499 (u
'\u01ce a with caron', 'a-a-with-caron'),
500 (u
'\u01d0 i with caron', 'i-i-with-caron'),
501 (u
'\u01d2 o with caron', 'o-o-with-caron'),
502 (u
'\u01d4 u with caron', 'u-u-with-caron'),
503 (u
'\u01e7 g with caron', 'g-g-with-caron'),
504 (u
'\u01e9 k with caron', 'k-k-with-caron'),
505 (u
'\u01eb o with ogonek', 'o-o-with-ogonek'),
506 (u
'\u01ed o with ogonek and macron', 'o-o-with-ogonek-and-macron'),
507 (u
'\u01f0 j with caron', 'j-j-with-caron'),
508 (u
'\u01f3 dz', 'dz-dz'),
509 (u
'\u01f5 g with acute', 'g-g-with-acute'),
510 (u
'\u01f9 n with grave', 'n-n-with-grave'),
511 (u
'\u0201 a with double grave', 'a-a-with-double-grave'),
512 (u
'\u0203 a with inverted breve', 'a-a-with-inverted-breve'),
513 (u
'\u0205 e with double grave', 'e-e-with-double-grave'),
514 (u
'\u0207 e with inverted breve', 'e-e-with-inverted-breve'),
515 (u
'\u0209 i with double grave', 'i-i-with-double-grave'),
516 (u
'\u020b i with inverted breve', 'i-i-with-inverted-breve'),
517 (u
'\u020d o with double grave', 'o-o-with-double-grave'),
518 (u
'\u020f o with inverted breve', 'o-o-with-inverted-breve'),
519 (u
'\u0211 r with double grave', 'r-r-with-double-grave'),
520 (u
'\u0213 r with inverted breve', 'r-r-with-inverted-breve'),
521 (u
'\u0215 u with double grave', 'u-u-with-double-grave'),
522 (u
'\u0217 u with inverted breve', 'u-u-with-inverted-breve'),
523 (u
'\u0219 s with comma below', 's-s-with-comma-below'),
524 (u
'\u021b t with comma below', 't-t-with-comma-below'),
525 (u
'\u021f h with caron', 'h-h-with-caron'),
526 (u
'\u0227 a with dot above', 'a-a-with-dot-above'),
527 (u
'\u0229 e with cedilla', 'e-e-with-cedilla'),
528 (u
'\u022f o with dot above', 'o-o-with-dot-above'),
529 (u
'\u0233 y with macron', 'y-y-with-macron'),
530 # digraphs From Latin-1 Supplements
531 (u
'\u00df: ligature sz', 'sz-ligature-sz'),
532 (u
'\u00e6 ae', 'ae-ae'),
533 (u
'\u0153 ligature oe', 'oe-ligature-oe'),
534 (u
'\u0238 db digraph', 'db-db-digraph'),
535 (u
'\u0239 qp digraph', 'qp-qp-digraph'),
538 def test_make_id(self
):
540 tests
= self
.ids
+ self
.ids_unicode_all
541 for input, expect
in tests
:
542 output
= nodes
.make_id(input)
544 failures
.append("'%s' != '%s'" % (expect
, output
))
546 self
.fail("%d failures in %d\n%s" % (len(failures
), len(self
.ids
), "\n".join(failures
)))
548 def test_traverse(self
):
551 e
[0] += nodes
.Element()
552 e
[0] += nodes
.TextElement()
553 e
[0][1] += nodes
.Text('some text')
556 self
.assertEqual(list(e
.traverse()),
557 [e
, e
[0], e
[0][0], e
[0][1], e
[0][1][0], e
[1], e
[2]])
558 self
.assertEqual(list(e
.traverse(include_self
=False)),
559 [e
[0], e
[0][0], e
[0][1], e
[0][1][0], e
[1], e
[2]])
560 self
.assertEqual(list(e
.traverse(descend
=False)),
562 self
.assertEqual(list(e
[0].traverse(descend
=False, ascend
=True)),
564 self
.assertEqual(list(e
[0][0].traverse(descend
=False, ascend
=True)),
565 [e
[0][0], e
[0][1], e
[1], e
[2]])
566 self
.assertEqual(list(e
[0][0].traverse(descend
=False, siblings
=True)),
568 self
.testlist
= e
[0:2]
569 self
.assertEqual(list(e
.traverse(condition
=self
.not_in_testlist
)),
570 [e
, e
[0][0], e
[0][1], e
[0][1][0], e
[2]])
571 # Return siblings despite siblings=False because ascend is true.
572 self
.assertEqual(list(e
[1].traverse(ascend
=True, siblings
=False)),
574 self
.assertEqual(list(e
[0].traverse()),
575 [e
[0], e
[0][0], e
[0][1], e
[0][1][0]])
576 self
.testlist
= [e
[0][0], e
[0][1]]
577 self
.assertEqual(list(e
[0].traverse(condition
=self
.not_in_testlist
)),
579 self
.testlist
.append(e
[0][1][0])
580 self
.assertEqual(list(e
[0].traverse(condition
=self
.not_in_testlist
)),
582 self
.assertEqual(list(e
.traverse(nodes
.TextElement
)), [e
[0][1]])
584 def test_next_node(self
):
587 e
[0] += nodes
.Element()
588 e
[0] += nodes
.TextElement()
589 e
[0][1] += nodes
.Text('some text')
592 self
.testlist
= [e
[0], e
[0][1], e
[1]]
593 compare
= [(e
, e
[0][0]),
595 (e
[0][0], e
[0][1][0]),
596 (e
[0][1], e
[0][1][0]),
600 for node
, next_node
in compare
:
601 self
.assertEqual(node
.next_node(self
.not_in_testlist
, ascend
=True),
603 self
.assertEqual(e
[0][0].next_node(ascend
=True), e
[0][1])
604 self
.assertEqual(e
[2].next_node(), None)
606 def not_in_testlist(self
, x
):
607 return x
not in self
.testlist
610 grandchild
= nodes
.Text('grandchild text')
611 child
= nodes
.emphasis('childtext', grandchild
, att
='child')
612 e
= nodes
.Element('raw text', child
, att
='e')
615 self
.assertTrue(e
is not e_copy
)
616 # Internal attributes (like `rawsource`) are also copied.
617 self
.assertEqual(e
.rawsource
, 'raw text')
618 self
.assertEqual(e_copy
.rawsource
, e
.rawsource
)
619 self
.assertEqual(e_copy
['att'], 'e')
620 self
.assertEqual(e_copy
.document
, e
.document
)
621 self
.assertEqual(e_copy
.source
, e
.source
)
622 self
.assertEqual(e_copy
.line
, e
.line
)
623 # Children are not copied.
624 self
.assertEqual(len(e_copy
), 0)
626 e_deepcopy
= e
.deepcopy()
627 self
.assertEqual(e_deepcopy
.rawsource
, e
.rawsource
)
628 self
.assertEqual(e_deepcopy
['att'], 'e')
629 # Children are copied recursively.
630 self
.assertEqual(e_deepcopy
[0][0], grandchild
)
631 self
.assertTrue(e_deepcopy
[0][0] is not grandchild
)
632 self
.assertEqual(e_deepcopy
[0]['att'], 'child')
634 def test_system_message_copy(self
):
635 e
= nodes
.system_message('mytext', att
='e', rawsource
='raw text')
638 self
.assertTrue(e
is not e_copy
)
639 # Internal attributes (like `rawsource`) are also copied.
640 self
.assertEqual(e
.rawsource
, 'raw text')
641 self
.assertEqual(e_copy
.rawsource
, e
.rawsource
)
642 self
.assertEqual(e_copy
['att'], 'e')
645 class TreeCopyVisitorTests(unittest
.TestCase
):
648 document
= utils
.new_document('test data')
649 document
+= nodes
.paragraph('', 'Paragraph 1.')
650 blist
= nodes
.bullet_list()
651 for i
in range(1, 6):
652 item
= nodes
.list_item()
653 for j
in range(1, 4):
654 item
+= nodes
.paragraph('', 'Item %s, paragraph %s.' % (i
, j
))
657 self
.document
= document
659 def compare_trees(self
, one
, two
):
660 self
.assertEqual(one
.__class
__, two
.__class
__)
661 self
.assertNotEqual(id(one
), id(two
))
662 self
.assertEqual(len(one
.children
), len(two
.children
))
663 for i
in range(len(one
.children
)):
664 self
.compare_trees(one
.children
[i
], two
.children
[i
])
666 def test_copy_whole(self
):
667 visitor
= nodes
.TreeCopyVisitor(self
.document
)
668 self
.document
.walkabout(visitor
)
669 newtree
= visitor
.get_tree_copy()
670 self
.assertEqual(self
.document
.pformat(), newtree
.pformat())
671 self
.compare_trees(self
.document
, newtree
)
674 class SetIdTests(unittest
.TestCase
):
677 self
.document
= utils
.new_document('test')
678 self
.elements
= [nodes
.Element(names
=['test']),
679 nodes
.section(), # Name empty
680 nodes
.section(names
=['Test']), # duplicate id
681 nodes
.footnote(names
=['2019-10-30']), # id empty
684 def test_set_id_default(self
):
686 for element
in self
.elements
:
687 self
.document
.set_id(element
)
688 ids
= [element
['ids'] for element
in self
.elements
]
689 self
.assertEqual(ids
, [['test'], ['id1'], ['id2'], ['id3']])
691 def test_set_id_custom(self
):
695 self
.document
.settings
.id_prefix
= 'P-'
696 self
.document
.settings
.auto_id_prefix
= 'auto'
698 for element
in self
.elements
:
699 self
.document
.set_id(element
)
700 ids
= [element
['ids'] for element
in self
.elements
]
701 self
.assertEqual(ids
, [['P-test'],
706 def test_set_id_descriptive_auto_id(self
):
707 # Use name or tag-name for auto-id.
710 self
.document
.settings
.auto_id_prefix
= '%'
712 for element
in self
.elements
:
713 self
.document
.set_id(element
)
714 ids
= [element
['ids'] for element
in self
.elements
]
715 self
.assertEqual(ids
, [['test'],
720 def test_set_id_custom_descriptive_auto_id(self
):
721 # Custom prefixes and name or tag-name for auto-id.
724 self
.document
.settings
.id_prefix
= 'P:'
725 self
.document
.settings
.auto_id_prefix
= 'a-%'
727 for element
in self
.elements
:
728 self
.document
.set_id(element
)
729 ids
= [element
['ids'] for element
in self
.elements
]
730 self
.assertEqual(ids
, [['P:test'],
736 class MiscFunctionTests(unittest
.TestCase
):
738 names
= [('a', 'a'), ('A', 'a'), ('A a A', 'a a a'),
739 ('A a A a', 'a a a a'),
740 (' AaA\n\r\naAa\tAaA\t\t', 'aaa aaa aaa')]
742 def test_normalize_name(self
):
743 for input, output
in self
.names
:
744 normed
= nodes
.fully_normalize_name(input)
745 self
.assertEqual(normed
, output
)
748 if __name__
== '__main__':