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