Edgewall Software

source: trunk/genshi/filters/tests/transform.py

Last change on this file was 1158, checked in by hodgestar, 13 years ago

Merge r1141 from py3k:

add support for python 3 to genshi.filters:

  • minor changes to track encoding=None API change in core genshi modules.
  • renamed genshi/filters/tests/html.py to test_html.py to avoid clashes with Python 3 top-level html module when running tests subset.
  • did not rename genshi/filters/html.py.
  • i18n filters:
    • ugettext and friends are gone in Python 3 (and only gettext and friends exist and they now handle unicode)
    • Some \ line continuations inside doctests confused 2to3 and so were removed them.
    • Testing picked up a problem (already present in trunk) where Translator.call could end up defining gettext as an endlessly recursive function. Noted with a TODO.
  • Property svn:eol-style set to native
File size: 46.2 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2007 Edgewall Software
4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://genshi.edgewall.org/wiki/License.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://genshi.edgewall.org/log/.
13
14import doctest
15from pprint import pprint
16import unittest
17
18from genshi import HTML
19from genshi.builder import Element
20from genshi.core import START, END, TEXT, QName, Attrs
21from genshi.filters.transform import Transformer, StreamBuffer, ENTER, EXIT, \
22                                     OUTSIDE, INSIDE, ATTR, BREAK
23import genshi.filters.transform
24
25
26FOO = '<root>ROOT<foo name="foo">FOO</foo></root>'
27FOOBAR = '<root>ROOT<foo name="foo" size="100">FOO</foo><bar name="bar">BAR</bar></root>'
28
29
30def _simplify(stream, with_attrs=False):
31    """Simplify a marked stream."""
32    def _generate():
33        for mark, (kind, data, pos) in stream:
34            if kind is START:
35                if with_attrs:
36                    data = (unicode(data[0]), dict((unicode(k), v)
37                                                   for k, v in data[1]))
38                else:
39                    data = unicode(data[0])
40            elif kind is END:
41                data = unicode(data)
42            elif kind is ATTR:
43                kind = ATTR
44                data = dict((unicode(k), v) for k, v in data[1])
45            yield mark, kind, data
46    return list(_generate())
47
48
49def _transform(html, transformer, with_attrs=False):
50    """Apply transformation returning simplified marked stream."""
51    if isinstance(html, basestring) and not isinstance(html, unicode):
52        html = HTML(html, encoding='utf-8')
53    elif isinstance(html, unicode):
54        html = HTML(html, encoding='utf-8')
55    stream = transformer(html, keep_marks=True)
56    return _simplify(stream, with_attrs)
57
58
59class SelectTest(unittest.TestCase):
60    """Test .select()"""
61    def _select(self, select):
62        html = HTML(FOOBAR, encoding='utf-8')
63        if isinstance(select, basestring):
64            select = [select]
65        transformer = Transformer(select[0])
66        for sel in select[1:]:
67            transformer = transformer.select(sel)
68        return _transform(html, transformer)
69
70    def test_select_single_element(self):
71        self.assertEqual(
72            self._select('foo'),
73            [(None, START, u'root'),
74             (None, TEXT, u'ROOT'),
75             (ENTER, START, u'foo'),
76             (INSIDE, TEXT, u'FOO'),
77             (EXIT, END, u'foo'),
78             (None, START, u'bar'),
79             (None, TEXT, u'BAR'),
80             (None, END, u'bar'),
81             (None, END, u'root')],
82            )
83
84    def test_select_context(self):
85        self.assertEqual(
86            self._select('.'),
87            [(ENTER, START, u'root'),
88             (INSIDE, TEXT, u'ROOT'),
89             (INSIDE, START, u'foo'),
90             (INSIDE, TEXT, u'FOO'),
91             (INSIDE, END, u'foo'),
92             (INSIDE, START, u'bar'),
93             (INSIDE, TEXT, u'BAR'),
94             (INSIDE, END, u'bar'),
95             (EXIT, END, u'root')]
96            )
97
98    def test_select_inside_select(self):
99        self.assertEqual(
100            self._select(['.', 'foo']),
101            [(None, START, u'root'),
102             (None, TEXT, u'ROOT'),
103             (ENTER, START, u'foo'),
104             (INSIDE, TEXT, u'FOO'),
105             (EXIT, END, u'foo'),
106             (None, START, u'bar'),
107             (None, TEXT, u'BAR'),
108             (None, END, u'bar'),
109             (None, END, u'root')],
110            )
111
112    def test_select_text(self):
113        self.assertEqual(
114            self._select('*/text()'),
115            [(None, START, u'root'),
116             (None, TEXT, u'ROOT'),
117             (None, START, u'foo'),
118             (OUTSIDE, TEXT, u'FOO'),
119             (None, END, u'foo'),
120             (None, START, u'bar'),
121             (OUTSIDE, TEXT, u'BAR'),
122             (None, END, u'bar'),
123             (None, END, u'root')],
124            )
125
126    def test_select_attr(self):
127        self.assertEqual(
128            self._select('foo/@name'),
129            [(None, START, u'root'),
130             (None, TEXT, u'ROOT'),
131             (ATTR, ATTR, {'name': u'foo'}),
132             (None, START, u'foo'),
133             (None, TEXT, u'FOO'),
134             (None, END, u'foo'),
135             (None, START, u'bar'),
136             (None, TEXT, u'BAR'),
137             (None, END, u'bar'),
138             (None, END, u'root')]
139            )
140
141    def test_select_text_context(self):
142        self.assertEqual(
143            list(Transformer('.')(HTML(u'foo'), keep_marks=True)),
144            [('OUTSIDE', ('TEXT', u'foo', (None, 1, 0)))],
145            )
146
147
148class InvertTest(unittest.TestCase):
149    def _invert(self, select):
150        return _transform(FOO, Transformer(select).invert())
151
152    def test_invert_element(self):
153        self.assertEqual(
154            self._invert('foo'),
155            [(OUTSIDE, START, u'root'),
156             (OUTSIDE, TEXT, u'ROOT'),
157             (None, START, u'foo'),
158             (None, TEXT, u'FOO'),
159             (None, END, u'foo'),
160             (OUTSIDE, END, u'root')]
161            )
162
163    def test_invert_inverted_element(self):
164        self.assertEqual(
165            _transform(FOO, Transformer('foo').invert().invert()),
166            [(None, START, u'root'),
167             (None, TEXT, u'ROOT'),
168             (OUTSIDE, START, u'foo'),
169             (OUTSIDE, TEXT, u'FOO'),
170             (OUTSIDE, END, u'foo'),
171             (None, END, u'root')]
172            )
173
174    def test_invert_text(self):
175        self.assertEqual(
176            self._invert('foo/text()'),
177            [(OUTSIDE, START, u'root'),
178             (OUTSIDE, TEXT, u'ROOT'),
179             (OUTSIDE, START, u'foo'),
180             (None, TEXT, u'FOO'),
181             (OUTSIDE, END, u'foo'),
182             (OUTSIDE, END, u'root')]
183            )
184
185    def test_invert_attribute(self):
186        self.assertEqual(
187            self._invert('foo/@name'),
188            [(OUTSIDE, START, u'root'),
189             (OUTSIDE, TEXT, u'ROOT'),
190             (None, ATTR, {'name': u'foo'}),
191             (OUTSIDE, START, u'foo'),
192             (OUTSIDE, TEXT, u'FOO'),
193             (OUTSIDE, END, u'foo'),
194             (OUTSIDE, END, u'root')]
195            )
196
197    def test_invert_context(self):
198        self.assertEqual(
199            self._invert('.'),
200            [(None, START, u'root'),
201             (None, TEXT, u'ROOT'),
202             (None, START, u'foo'),
203             (None, TEXT, u'FOO'),
204             (None, END, u'foo'),
205             (None, END, u'root')]
206            )
207
208    def test_invert_text_context(self):
209        self.assertEqual(
210            _simplify(Transformer('.').invert()(HTML(u'foo'), keep_marks=True)),
211            [(None, 'TEXT', u'foo')],
212            )
213
214
215
216class EndTest(unittest.TestCase):
217    def test_end(self):
218        stream = _transform(FOO, Transformer('foo').end())
219        self.assertEqual(
220            stream,
221            [(OUTSIDE, START, u'root'),
222             (OUTSIDE, TEXT, u'ROOT'),
223             (OUTSIDE, START, u'foo'),
224             (OUTSIDE, TEXT, u'FOO'),
225             (OUTSIDE, END, u'foo'),
226             (OUTSIDE, END, u'root')]
227            )
228
229
230class EmptyTest(unittest.TestCase):
231    def _empty(self, select):
232        return _transform(FOO, Transformer(select).empty())
233
234    def test_empty_element(self):
235        self.assertEqual(
236            self._empty('foo'),
237            [(None, START, u'root'),
238             (None, TEXT, u'ROOT'),
239             (ENTER, START, u'foo'),
240             (EXIT, END, u'foo'),
241             (None, END, u'root')],
242            )
243
244    def test_empty_text(self):
245        self.assertEqual(
246            self._empty('foo/text()'),
247            [(None, START, u'root'),
248             (None, TEXT, u'ROOT'),
249             (None, START, u'foo'),
250             (OUTSIDE, TEXT, u'FOO'),
251             (None, END, u'foo'),
252             (None, END, u'root')]
253            )
254
255    def test_empty_attr(self):
256        self.assertEqual(
257            self._empty('foo/@name'),
258            [(None, START, u'root'),
259             (None, TEXT, u'ROOT'),
260             (ATTR, ATTR, {'name': u'foo'}),
261             (None, START, u'foo'),
262             (None, TEXT, u'FOO'),
263             (None, END, u'foo'),
264             (None, END, u'root')]
265            )
266
267    def test_empty_context(self):
268        self.assertEqual(
269            self._empty('.'),
270            [(ENTER, START, u'root'),
271             (EXIT, END, u'root')]
272            )
273
274    def test_empty_text_context(self):
275        self.assertEqual(
276            _simplify(Transformer('.')(HTML(u'foo'), keep_marks=True)),
277            [(OUTSIDE, TEXT, u'foo')],
278            )
279
280
281class RemoveTest(unittest.TestCase):
282    def _remove(self, select):
283        return _transform(FOO, Transformer(select).remove())
284
285    def test_remove_element(self):
286        self.assertEqual(
287            self._remove('foo|bar'),
288            [(None, START, u'root'),
289             (None, TEXT, u'ROOT'),
290             (None, END, u'root')]
291            )
292
293    def test_remove_text(self):
294        self.assertEqual(
295            self._remove('//text()'),
296            [(None, START, u'root'),
297             (None, START, u'foo'),
298             (None, END, u'foo'),
299             (None, END, u'root')]
300            )
301
302    def test_remove_attr(self):
303        self.assertEqual(
304            self._remove('foo/@name'),
305            [(None, START, u'root'),
306             (None, TEXT, u'ROOT'),
307             (None, START, u'foo'),
308             (None, TEXT, u'FOO'),
309             (None, END, u'foo'),
310             (None, END, u'root')]
311            )
312
313    def test_remove_context(self):
314        self.assertEqual(
315            self._remove('.'),
316            [],
317            )
318
319    def test_remove_text_context(self):
320        self.assertEqual(
321            _transform('foo', Transformer('.').remove()),
322            [],
323            )
324
325
326class UnwrapText(unittest.TestCase):
327    def _unwrap(self, select):
328        return _transform(FOO, Transformer(select).unwrap())
329
330    def test_unwrap_element(self):
331        self.assertEqual(
332            self._unwrap('foo'),
333            [(None, START, u'root'),
334             (None, TEXT, u'ROOT'),
335             (INSIDE, TEXT, u'FOO'),
336             (None, END, u'root')]
337            )
338
339    def test_unwrap_text(self):
340        self.assertEqual(
341            self._unwrap('foo/text()'),
342            [(None, START, u'root'),
343             (None, TEXT, u'ROOT'),
344             (None, START, u'foo'),
345             (OUTSIDE, TEXT, u'FOO'),
346             (None, END, u'foo'),
347             (None, END, u'root')]
348            )
349
350    def test_unwrap_attr(self):
351        self.assertEqual(
352            self._unwrap('foo/@name'),
353            [(None, START, u'root'),
354             (None, TEXT, u'ROOT'),
355             (ATTR, ATTR, {'name': u'foo'}),
356             (None, START, u'foo'),
357             (None, TEXT, u'FOO'),
358             (None, END, u'foo'),
359             (None, END, u'root')]
360            )
361
362    def test_unwrap_adjacent(self):
363        self.assertEqual(
364            _transform(FOOBAR, Transformer('foo|bar').unwrap()),
365            [(None, START, u'root'),
366             (None, TEXT, u'ROOT'),
367             (INSIDE, TEXT, u'FOO'),
368             (INSIDE, TEXT, u'BAR'),
369             (None, END, u'root')]
370            )
371
372    def test_unwrap_root(self):
373        self.assertEqual(
374            self._unwrap('.'),
375            [(INSIDE, TEXT, u'ROOT'),
376             (INSIDE, START, u'foo'),
377             (INSIDE, TEXT, u'FOO'),
378             (INSIDE, END, u'foo')]
379            )
380
381    def test_unwrap_text_root(self):
382        self.assertEqual(
383            _transform('foo', Transformer('.').unwrap()),
384            [(OUTSIDE, TEXT, 'foo')],
385            )
386
387
388class WrapTest(unittest.TestCase):
389    def _wrap(self, select, wrap='wrap'):
390        return _transform(FOO, Transformer(select).wrap(wrap))
391
392    def test_wrap_element(self):
393        self.assertEqual(
394            self._wrap('foo'),
395            [(None, START, u'root'),
396             (None, TEXT, u'ROOT'),
397             (None, START, u'wrap'),
398             (ENTER, START, u'foo'),
399             (INSIDE, TEXT, u'FOO'),
400             (EXIT, END, u'foo'),
401             (None, END, u'wrap'),
402             (None, END, u'root')]
403            )
404
405    def test_wrap_adjacent_elements(self):
406        self.assertEqual(
407            _transform(FOOBAR, Transformer('foo|bar').wrap('wrap')),
408            [(None, START, u'root'),
409             (None, TEXT, u'ROOT'),
410             (None, START, u'wrap'),
411             (ENTER, START, u'foo'),
412             (INSIDE, TEXT, u'FOO'),
413             (EXIT, END, u'foo'),
414             (None, END, u'wrap'),
415             (None, START, u'wrap'),
416             (ENTER, START, u'bar'),
417             (INSIDE, TEXT, u'BAR'),
418             (EXIT, END, u'bar'),
419             (None, END, u'wrap'),
420             (None, END, u'root')]
421            )
422
423    def test_wrap_text(self):
424        self.assertEqual(
425            self._wrap('foo/text()'),
426            [(None, START, u'root'),
427             (None, TEXT, u'ROOT'),
428             (None, START, u'foo'),
429             (None, START, u'wrap'),
430             (OUTSIDE, TEXT, u'FOO'),
431             (None, END, u'wrap'),
432             (None, END, u'foo'),
433             (None, END, u'root')]
434            )
435
436    def test_wrap_root(self):
437        self.assertEqual(
438            self._wrap('.'),
439            [(None, START, u'wrap'),
440             (ENTER, START, u'root'),
441             (INSIDE, TEXT, u'ROOT'),
442             (INSIDE, START, u'foo'),
443             (INSIDE, TEXT, u'FOO'),
444             (INSIDE, END, u'foo'),
445             (EXIT, END, u'root'),
446             (None, END, u'wrap')]
447            )
448
449    def test_wrap_text_root(self):
450        self.assertEqual(
451            _transform('foo', Transformer('.').wrap('wrap')),
452            [(None, START, u'wrap'),
453             (OUTSIDE, TEXT, u'foo'),
454             (None, END, u'wrap')],
455            )
456
457    def test_wrap_with_element(self):
458        element = Element('a', href='http://localhost')
459        self.assertEqual(
460            _transform('foo', Transformer('.').wrap(element), with_attrs=True),
461            [(None, START, (u'a', {u'href': u'http://localhost'})),
462             (OUTSIDE, TEXT, u'foo'),
463             (None, END, u'a')]
464            )
465
466
467class FilterTest(unittest.TestCase):
468    def _filter(self, select, html=FOOBAR):
469        """Returns a list of lists of filtered elements."""
470        output = []
471        def filtered(stream):
472            interval = []
473            output.append(interval)
474            for event in stream:
475                interval.append(event)
476                yield event
477        _transform(html, Transformer(select).filter(filtered))
478        simplified = []
479        for sub in output:
480            simplified.append(_simplify([(None, event) for event in sub]))
481        return simplified
482
483    def test_filter_element(self):
484        self.assertEqual(
485            self._filter('foo'),
486            [[(None, START, u'foo'),
487              (None, TEXT, u'FOO'),
488              (None, END, u'foo')]]
489            )
490
491    def test_filter_adjacent_elements(self):
492        self.assertEqual(
493            self._filter('foo|bar'),
494            [[(None, START, u'foo'),
495              (None, TEXT, u'FOO'),
496              (None, END, u'foo')],
497             [(None, START, u'bar'),
498              (None, TEXT, u'BAR'),
499              (None, END, u'bar')]]
500            )
501
502    def test_filter_text(self):
503        self.assertEqual(
504            self._filter('*/text()'),
505            [[(None, TEXT, u'FOO')],
506             [(None, TEXT, u'BAR')]]
507            )
508    def test_filter_root(self):
509        self.assertEqual(
510            self._filter('.'),
511            [[(None, START, u'root'),
512              (None, TEXT, u'ROOT'),
513              (None, START, u'foo'),
514              (None, TEXT, u'FOO'),
515              (None, END, u'foo'),
516              (None, START, u'bar'),
517              (None, TEXT, u'BAR'),
518              (None, END, u'bar'),
519              (None, END, u'root')]]
520            )
521
522    def test_filter_text_root(self):
523        self.assertEqual(
524            self._filter('.', 'foo'),
525            [[(None, TEXT, u'foo')]])
526
527    def test_filter_after_outside(self):
528        stream = _transform(
529            '<root>x</root>', Transformer('//root/text()').filter(lambda x: x))
530        self.assertEqual(
531            list(stream),
532            [(None, START, u'root'),
533             (OUTSIDE, TEXT, u'x'),
534             (None, END, u'root')])
535
536
537class MapTest(unittest.TestCase):
538    def _map(self, select, kind=None):
539        data = []
540        def record(d):
541            data.append(d)
542            return d
543        _transform(FOOBAR, Transformer(select).map(record, kind))
544        return data
545
546    def test_map_element(self):
547        self.assertEqual(
548            self._map('foo'),
549            [(QName('foo'), Attrs([(QName('name'), u'foo'),
550                                   (QName('size'), u'100')])),
551             u'FOO',
552             QName('foo')]
553        )
554
555    def test_map_with_text_kind(self):
556        self.assertEqual(
557            self._map('.', TEXT),
558            [u'ROOT', u'FOO', u'BAR']
559        )
560
561    def test_map_with_root_and_end_kind(self):
562        self.assertEqual(
563            self._map('.', END),
564            [QName('foo'), QName('bar'), QName('root')]
565        )
566
567    def test_map_with_attribute(self):
568        self.assertEqual(
569            self._map('foo/@name'),
570            [(QName('foo@*'), Attrs([('name', u'foo')]))]
571        )
572
573
574class SubstituteTest(unittest.TestCase):
575    def _substitute(self, select, pattern, replace):
576        return _transform(FOOBAR, Transformer(select).substitute(pattern, replace))
577
578    def test_substitute_foo(self):
579        self.assertEqual(
580            self._substitute('foo', 'FOO|BAR', 'FOOOOO'),
581            [(None, START, u'root'),
582             (None, TEXT, u'ROOT'),
583             (ENTER, START, u'foo'),
584             (INSIDE, TEXT, u'FOOOOO'),
585             (EXIT, END, u'foo'),
586             (None, START, u'bar'),
587             (None, TEXT, u'BAR'),
588             (None, END, u'bar'),
589             (None, END, u'root')]
590            )
591
592    def test_substitute_foobar_with_group(self):
593        self.assertEqual(
594            self._substitute('foo|bar', '(FOO|BAR)', r'(\1)'),
595            [(None, START, u'root'),
596             (None, TEXT, u'ROOT'),
597             (ENTER, START, u'foo'),
598             (INSIDE, TEXT, u'(FOO)'),
599             (EXIT, END, u'foo'),
600             (ENTER, START, u'bar'),
601             (INSIDE, TEXT, u'(BAR)'),
602             (EXIT, END, u'bar'),
603             (None, END, u'root')]
604            )
605
606
607class RenameTest(unittest.TestCase):
608    def _rename(self, select):
609        return _transform(FOOBAR, Transformer(select).rename('foobar'))
610
611    def test_rename_root(self):
612        self.assertEqual(
613            self._rename('.'),
614            [(ENTER, START, u'foobar'),
615             (INSIDE, TEXT, u'ROOT'),
616             (INSIDE, START, u'foo'),
617             (INSIDE, TEXT, u'FOO'),
618             (INSIDE, END, u'foo'),
619             (INSIDE, START, u'bar'),
620             (INSIDE, TEXT, u'BAR'),
621             (INSIDE, END, u'bar'),
622             (EXIT, END, u'foobar')]
623            )
624
625    def test_rename_element(self):
626        self.assertEqual(
627            self._rename('foo|bar'),
628            [(None, START, u'root'),
629             (None, TEXT, u'ROOT'),
630             (ENTER, START, u'foobar'),
631             (INSIDE, TEXT, u'FOO'),
632             (EXIT, END, u'foobar'),
633             (ENTER, START, u'foobar'),
634             (INSIDE, TEXT, u'BAR'),
635             (EXIT, END, u'foobar'),
636             (None, END, u'root')]
637            )
638
639    def test_rename_text(self):
640        self.assertEqual(
641            self._rename('foo/text()'),
642            [(None, START, u'root'),
643             (None, TEXT, u'ROOT'),
644             (None, START, u'foo'),
645             (OUTSIDE, TEXT, u'FOO'),
646             (None, END, u'foo'),
647             (None, START, u'bar'),
648             (None, TEXT, u'BAR'),
649             (None, END, u'bar'),
650             (None, END, u'root')]
651            )
652
653
654class ContentTestMixin(object):
655    def _apply(self, select, content=None, html=FOOBAR):
656        class Injector(object):
657            count = 0
658
659            def __iter__(self):
660                self.count += 1
661                return iter(HTML(u'CONTENT %i' % self.count))
662
663        if isinstance(html, basestring) and not isinstance(html, unicode):
664            html = HTML(html, encoding='utf-8')
665        else:
666            html = HTML(html)
667        if content is None:
668            content = Injector()
669        elif isinstance(content, basestring):
670            content = HTML(content)
671        return _transform(html, getattr(Transformer(select), self.operation)
672                                (content))
673
674
675class ReplaceTest(unittest.TestCase, ContentTestMixin):
676    operation = 'replace'
677
678    def test_replace_element(self):
679        self.assertEqual(
680            self._apply('foo'),
681            [(None, START, u'root'),
682             (None, TEXT, u'ROOT'),
683             (None, TEXT, u'CONTENT 1'),
684             (None, START, u'bar'),
685             (None, TEXT, u'BAR'),
686             (None, END, u'bar'),
687             (None, END, u'root')]
688            )
689
690    def test_replace_text(self):
691        self.assertEqual(
692            self._apply('text()'),
693            [(None, START, u'root'),
694             (None, TEXT, u'CONTENT 1'),
695             (None, START, u'foo'),
696             (None, TEXT, u'FOO'),
697             (None, END, u'foo'),
698             (None, START, u'bar'),
699             (None, TEXT, u'BAR'),
700             (None, END, u'bar'),
701             (None, END, u'root')]
702            )
703
704    def test_replace_context(self):
705        self.assertEqual(
706            self._apply('.'),
707            [(None, TEXT, u'CONTENT 1')],
708            )
709
710    def test_replace_text_context(self):
711        self.assertEqual(
712            self._apply('.', html='foo'),
713            [(None, TEXT, u'CONTENT 1')],
714            )
715
716    def test_replace_adjacent_elements(self):
717        self.assertEqual(
718            self._apply('*'),
719            [(None, START, u'root'),
720             (None, TEXT, u'ROOT'),
721             (None, TEXT, u'CONTENT 1'),
722             (None, TEXT, u'CONTENT 2'),
723             (None, END, u'root')],
724            )
725
726    def test_replace_all(self):
727        self.assertEqual(
728            self._apply('*|text()'),
729            [(None, START, u'root'),
730             (None, TEXT, u'CONTENT 1'),
731             (None, TEXT, u'CONTENT 2'),
732             (None, TEXT, u'CONTENT 3'),
733             (None, END, u'root')],
734            )
735
736    def test_replace_with_callback(self):
737        count = [0]
738        def content():
739            count[0] += 1
740            yield '%2i.' % count[0]
741        self.assertEqual(
742            self._apply('*', content),
743            [(None, START, u'root'),
744             (None, TEXT, u'ROOT'),
745             (None, TEXT, u' 1.'),
746             (None, TEXT, u' 2.'),
747             (None, END, u'root')]
748            )
749
750
751class BeforeTest(unittest.TestCase, ContentTestMixin):
752    operation = 'before'
753
754    def test_before_element(self):
755        self.assertEqual(
756            self._apply('foo'),
757            [(None, START, u'root'),
758             (None, TEXT, u'ROOT'),
759             (None, TEXT, u'CONTENT 1'),
760             (ENTER, START, u'foo'),
761             (INSIDE, TEXT, u'FOO'),
762             (EXIT, END, u'foo'),
763             (None, START, u'bar'),
764             (None, TEXT, u'BAR'),
765             (None, END, u'bar'),
766             (None, END, u'root')]
767            )
768
769    def test_before_text(self):
770        self.assertEqual(
771            self._apply('text()'),
772            [(None, START, u'root'),
773             (None, TEXT, u'CONTENT 1'),
774             (OUTSIDE, TEXT, u'ROOT'),
775             (None, START, u'foo'),
776             (None, TEXT, u'FOO'),
777             (None, END, u'foo'),
778             (None, START, u'bar'),
779             (None, TEXT, u'BAR'),
780             (None, END, u'bar'),
781             (None, END, u'root')]
782            )
783
784    def test_before_context(self):
785        self.assertEqual(
786            self._apply('.'),
787            [(None, TEXT, u'CONTENT 1'),
788             (ENTER, START, u'root'),
789             (INSIDE, TEXT, u'ROOT'),
790             (INSIDE, START, u'foo'),
791             (INSIDE, TEXT, u'FOO'),
792             (INSIDE, END, u'foo'),
793             (INSIDE, START, u'bar'),
794             (INSIDE, TEXT, u'BAR'),
795             (INSIDE, END, u'bar'),
796             (EXIT, END, u'root')]
797            )
798
799    def test_before_text_context(self):
800        self.assertEqual(
801            self._apply('.', html='foo'),
802            [(None, TEXT, u'CONTENT 1'),
803             (OUTSIDE, TEXT, u'foo')]
804            )
805
806    def test_before_adjacent_elements(self):
807        self.assertEqual(
808            self._apply('*'),
809            [(None, START, u'root'),
810             (None, TEXT, u'ROOT'),
811             (None, TEXT, u'CONTENT 1'),
812             (ENTER, START, u'foo'),
813             (INSIDE, TEXT, u'FOO'),
814             (EXIT, END, u'foo'),
815             (None, TEXT, u'CONTENT 2'),
816             (ENTER, START, u'bar'),
817             (INSIDE, TEXT, u'BAR'),
818             (EXIT, END, u'bar'),
819             (None, END, u'root')]
820
821            )
822
823    def test_before_all(self):
824        self.assertEqual(
825            self._apply('*|text()'),
826            [(None, START, u'root'),
827             (None, TEXT, u'CONTENT 1'),
828             (OUTSIDE, TEXT, u'ROOT'),
829             (None, TEXT, u'CONTENT 2'),
830             (ENTER, START, u'foo'),
831             (INSIDE, TEXT, u'FOO'),
832             (EXIT, END, u'foo'),
833             (None, TEXT, u'CONTENT 3'),
834             (ENTER, START, u'bar'),
835             (INSIDE, TEXT, u'BAR'),
836             (EXIT, END, u'bar'),
837             (None, END, u'root')]
838            )
839
840    def test_before_with_callback(self):
841        count = [0]
842        def content():
843            count[0] += 1
844            yield '%2i.' % count[0]
845        self.assertEqual(
846            self._apply('foo/text()', content),
847            [(None, 'START', u'root'),
848             (None, 'TEXT', u'ROOT'),
849             (None, 'START', u'foo'),
850             (None, 'TEXT', u' 1.'),
851             ('OUTSIDE', 'TEXT', u'FOO'),
852             (None, 'END', u'foo'),
853             (None, 'START', u'bar'),
854             (None, 'TEXT', u'BAR'),
855             (None, 'END', u'bar'),
856             (None, 'END', u'root')]
857            )
858
859
860class AfterTest(unittest.TestCase, ContentTestMixin):
861    operation = 'after'
862
863    def test_after_element(self):
864        self.assertEqual(
865            self._apply('foo'),
866            [(None, START, u'root'),
867             (None, TEXT, u'ROOT'),
868             (ENTER, START, u'foo'),
869             (INSIDE, TEXT, u'FOO'),
870             (EXIT, END, u'foo'),
871             (None, TEXT, u'CONTENT 1'),
872             (None, START, u'bar'),
873             (None, TEXT, u'BAR'),
874             (None, END, u'bar'),
875             (None, END, u'root')]
876            )
877
878    def test_after_text(self):
879        self.assertEqual(
880            self._apply('text()'),
881            [(None, START, u'root'),
882             (OUTSIDE, TEXT, u'ROOT'),
883             (None, TEXT, u'CONTENT 1'),
884             (None, START, u'foo'),
885             (None, TEXT, u'FOO'),
886             (None, END, u'foo'),
887             (None, START, u'bar'),
888             (None, TEXT, u'BAR'),
889             (None, END, u'bar'),
890             (None, END, u'root')]
891            )
892
893    def test_after_context(self):
894        self.assertEqual(
895            self._apply('.'),
896            [(ENTER, START, u'root'),
897             (INSIDE, TEXT, u'ROOT'),
898             (INSIDE, START, u'foo'),
899             (INSIDE, TEXT, u'FOO'),
900             (INSIDE, END, u'foo'),
901             (INSIDE, START, u'bar'),
902             (INSIDE, TEXT, u'BAR'),
903             (INSIDE, END, u'bar'),
904             (EXIT, END, u'root'),
905             (None, TEXT, u'CONTENT 1')]
906            )
907
908    def test_after_text_context(self):
909        self.assertEqual(
910            self._apply('.', html='foo'),
911            [(OUTSIDE, TEXT, u'foo'),
912             (None, TEXT, u'CONTENT 1')]
913            )
914
915    def test_after_adjacent_elements(self):
916        self.assertEqual(
917            self._apply('*'),
918            [(None, START, u'root'),
919             (None, TEXT, u'ROOT'),
920             (ENTER, START, u'foo'),
921             (INSIDE, TEXT, u'FOO'),
922             (EXIT, END, u'foo'),
923             (None, TEXT, u'CONTENT 1'),
924             (ENTER, START, u'bar'),
925             (INSIDE, TEXT, u'BAR'),
926             (EXIT, END, u'bar'),
927             (None, TEXT, u'CONTENT 2'),
928             (None, END, u'root')]
929
930            )
931
932    def test_after_all(self):
933        self.assertEqual(
934            self._apply('*|text()'),
935            [(None, START, u'root'),
936             (OUTSIDE, TEXT, u'ROOT'),
937             (None, TEXT, u'CONTENT 1'),
938             (ENTER, START, u'foo'),
939             (INSIDE, TEXT, u'FOO'),
940             (EXIT, END, u'foo'),
941             (None, TEXT, u'CONTENT 2'),
942             (ENTER, START, u'bar'),
943             (INSIDE, TEXT, u'BAR'),
944             (EXIT, END, u'bar'),
945             (None, TEXT, u'CONTENT 3'),
946             (None, END, u'root')]
947            )
948
949    def test_after_with_callback(self):
950        count = [0]
951        def content():
952            count[0] += 1
953            yield '%2i.' % count[0]
954        self.assertEqual(
955            self._apply('foo/text()', content),
956            [(None, 'START', u'root'),
957             (None, 'TEXT', u'ROOT'),
958             (None, 'START', u'foo'),
959             ('OUTSIDE', 'TEXT', u'FOO'),
960             (None, 'TEXT', u' 1.'),
961             (None, 'END', u'foo'),
962             (None, 'START', u'bar'),
963             (None, 'TEXT', u'BAR'),
964             (None, 'END', u'bar'),
965             (None, 'END', u'root')]
966            )
967
968
969class PrependTest(unittest.TestCase, ContentTestMixin):
970    operation = 'prepend'
971
972    def test_prepend_element(self):
973        self.assertEqual(
974            self._apply('foo'),
975            [(None, START, u'root'),
976             (None, TEXT, u'ROOT'),
977             (ENTER, START, u'foo'),
978             (None, TEXT, u'CONTENT 1'),
979             (INSIDE, TEXT, u'FOO'),
980             (EXIT, END, u'foo'),
981             (None, START, u'bar'),
982             (None, TEXT, u'BAR'),
983             (None, END, u'bar'),
984             (None, END, u'root')]
985            )
986
987    def test_prepend_text(self):
988        self.assertEqual(
989            self._apply('text()'),
990            [(None, START, u'root'),
991             (OUTSIDE, TEXT, u'ROOT'),
992             (None, START, u'foo'),
993             (None, TEXT, u'FOO'),
994             (None, END, u'foo'),
995             (None, START, u'bar'),
996             (None, TEXT, u'BAR'),
997             (None, END, u'bar'),
998             (None, END, u'root')]
999            )
1000
1001    def test_prepend_context(self):
1002        self.assertEqual(
1003            self._apply('.'),
1004            [(ENTER, START, u'root'),
1005             (None, TEXT, u'CONTENT 1'),
1006             (INSIDE, TEXT, u'ROOT'),
1007             (INSIDE, START, u'foo'),
1008             (INSIDE, TEXT, u'FOO'),
1009             (INSIDE, END, u'foo'),
1010             (INSIDE, START, u'bar'),
1011             (INSIDE, TEXT, u'BAR'),
1012             (INSIDE, END, u'bar'),
1013             (EXIT, END, u'root')],
1014            )
1015
1016    def test_prepend_text_context(self):
1017        self.assertEqual(
1018            self._apply('.', html='foo'),
1019            [(OUTSIDE, TEXT, u'foo')]
1020            )
1021
1022    def test_prepend_adjacent_elements(self):
1023        self.assertEqual(
1024            self._apply('*'),
1025            [(None, START, u'root'),
1026             (None, TEXT, u'ROOT'),
1027             (ENTER, START, u'foo'),
1028             (None, TEXT, u'CONTENT 1'),
1029             (INSIDE, TEXT, u'FOO'),
1030             (EXIT, END, u'foo'),
1031             (ENTER, START, u'bar'),
1032             (None, TEXT, u'CONTENT 2'),
1033             (INSIDE, TEXT, u'BAR'),
1034             (EXIT, END, u'bar'),
1035             (None, END, u'root')]
1036
1037            )
1038
1039    def test_prepend_all(self):
1040        self.assertEqual(
1041            self._apply('*|text()'),
1042            [(None, START, u'root'),
1043             (OUTSIDE, TEXT, u'ROOT'),
1044             (ENTER, START, u'foo'),
1045             (None, TEXT, u'CONTENT 1'),
1046             (INSIDE, TEXT, u'FOO'),
1047             (EXIT, END, u'foo'),
1048             (ENTER, START, u'bar'),
1049             (None, TEXT, u'CONTENT 2'),
1050             (INSIDE, TEXT, u'BAR'),
1051             (EXIT, END, u'bar'),
1052             (None, END, u'root')]
1053            )
1054
1055    def test_prepend_with_callback(self):
1056        count = [0]
1057        def content():
1058            count[0] += 1
1059            yield '%2i.' % count[0]
1060        self.assertEqual(
1061            self._apply('foo', content),
1062            [(None, 'START', u'root'),
1063             (None, 'TEXT', u'ROOT'),
1064             (ENTER, 'START', u'foo'),
1065             (None, 'TEXT', u' 1.'),
1066             (INSIDE, 'TEXT', u'FOO'),
1067             (EXIT, 'END', u'foo'),
1068             (None, 'START', u'bar'),
1069             (None, 'TEXT', u'BAR'),
1070             (None, 'END', u'bar'),
1071             (None, 'END', u'root')]
1072            )
1073
1074
1075class AppendTest(unittest.TestCase, ContentTestMixin):
1076    operation = 'append'
1077
1078    def test_append_element(self):
1079        self.assertEqual(
1080            self._apply('foo'),
1081            [(None, START, u'root'),
1082             (None, TEXT, u'ROOT'),
1083             (ENTER, START, u'foo'),
1084             (INSIDE, TEXT, u'FOO'),
1085             (None, TEXT, u'CONTENT 1'),
1086             (EXIT, END, u'foo'),
1087             (None, START, u'bar'),
1088             (None, TEXT, u'BAR'),
1089             (None, END, u'bar'),
1090             (None, END, u'root')]
1091            )
1092
1093    def test_append_text(self):
1094        self.assertEqual(
1095            self._apply('text()'),
1096            [(None, START, u'root'),
1097             (OUTSIDE, TEXT, u'ROOT'),
1098             (None, START, u'foo'),
1099             (None, TEXT, u'FOO'),
1100             (None, END, u'foo'),
1101             (None, START, u'bar'),
1102             (None, TEXT, u'BAR'),
1103             (None, END, u'bar'),
1104             (None, END, u'root')]
1105            )
1106
1107    def test_append_context(self):
1108        self.assertEqual(
1109            self._apply('.'),
1110            [(ENTER, START, u'root'),
1111             (INSIDE, TEXT, u'ROOT'),
1112             (INSIDE, START, u'foo'),
1113             (INSIDE, TEXT, u'FOO'),
1114             (INSIDE, END, u'foo'),
1115             (INSIDE, START, u'bar'),
1116             (INSIDE, TEXT, u'BAR'),
1117             (INSIDE, END, u'bar'),
1118             (None, TEXT, u'CONTENT 1'),
1119             (EXIT, END, u'root')],
1120            )
1121
1122    def test_append_text_context(self):
1123        self.assertEqual(
1124            self._apply('.', html='foo'),
1125            [(OUTSIDE, TEXT, u'foo')]
1126            )
1127
1128    def test_append_adjacent_elements(self):
1129        self.assertEqual(
1130            self._apply('*'),
1131            [(None, START, u'root'),
1132             (None, TEXT, u'ROOT'),
1133             (ENTER, START, u'foo'),
1134             (INSIDE, TEXT, u'FOO'),
1135             (None, TEXT, u'CONTENT 1'),
1136             (EXIT, END, u'foo'),
1137             (ENTER, START, u'bar'),
1138             (INSIDE, TEXT, u'BAR'),
1139             (None, TEXT, u'CONTENT 2'),
1140             (EXIT, END, u'bar'),
1141             (None, END, u'root')]
1142
1143            )
1144
1145    def test_append_all(self):
1146        self.assertEqual(
1147            self._apply('*|text()'),
1148            [(None, START, u'root'),
1149             (OUTSIDE, TEXT, u'ROOT'),
1150             (ENTER, START, u'foo'),
1151             (INSIDE, TEXT, u'FOO'),
1152             (None, TEXT, u'CONTENT 1'),
1153             (EXIT, END, u'foo'),
1154             (ENTER, START, u'bar'),
1155             (INSIDE, TEXT, u'BAR'),
1156             (None, TEXT, u'CONTENT 2'),
1157             (EXIT, END, u'bar'),
1158             (None, END, u'root')]
1159            )
1160
1161    def test_append_with_callback(self):
1162        count = [0]
1163        def content():
1164            count[0] += 1
1165            yield '%2i.' % count[0]
1166        self.assertEqual(
1167            self._apply('foo', content),
1168            [(None, 'START', u'root'),
1169             (None, 'TEXT', u'ROOT'),
1170             (ENTER, 'START', u'foo'),
1171             (INSIDE, 'TEXT', u'FOO'),
1172             (None, 'TEXT', u' 1.'),
1173             (EXIT, 'END', u'foo'),
1174             (None, 'START', u'bar'),
1175             (None, 'TEXT', u'BAR'),
1176             (None, 'END', u'bar'),
1177             (None, 'END', u'root')]
1178            )
1179
1180
1181
1182class AttrTest(unittest.TestCase):
1183    def _attr(self, select, name, value):
1184        return _transform(FOOBAR, Transformer(select).attr(name, value),
1185                          with_attrs=True)
1186
1187    def test_set_existing_attr(self):
1188        self.assertEqual(
1189            self._attr('foo', 'name', 'FOO'),
1190            [(None, START, (u'root', {})),
1191             (None, TEXT, u'ROOT'),
1192             (ENTER, START, (u'foo', {u'name': 'FOO', u'size': '100'})),
1193             (INSIDE, TEXT, u'FOO'),
1194             (EXIT, END, u'foo'),
1195             (None, START, (u'bar', {u'name': u'bar'})),
1196             (None, TEXT, u'BAR'),
1197             (None, END, u'bar'),
1198             (None, END, u'root')]
1199            )
1200
1201    def test_set_new_attr(self):
1202        self.assertEqual(
1203            self._attr('foo', 'title', 'FOO'),
1204            [(None, START, (u'root', {})),
1205             (None, TEXT, u'ROOT'),
1206             (ENTER, START, (u'foo', {u'name': u'foo', u'title': 'FOO', u'size': '100'})),
1207             (INSIDE, TEXT, u'FOO'),
1208             (EXIT, END, u'foo'),
1209             (None, START, (u'bar', {u'name': u'bar'})),
1210             (None, TEXT, u'BAR'),
1211             (None, END, u'bar'),
1212             (None, END, u'root')]
1213            )
1214
1215    def test_attr_from_function(self):
1216        def set(name, event):
1217            self.assertEqual(name, 'name')
1218            return event[1][1].get('name').upper()
1219
1220        self.assertEqual(
1221            self._attr('foo|bar', 'name', set),
1222            [(None, START, (u'root', {})),
1223             (None, TEXT, u'ROOT'),
1224             (ENTER, START, (u'foo', {u'name': 'FOO', u'size': '100'})),
1225             (INSIDE, TEXT, u'FOO'),
1226             (EXIT, END, u'foo'),
1227             (ENTER, START, (u'bar', {u'name': 'BAR'})),
1228             (INSIDE, TEXT, u'BAR'),
1229             (EXIT, END, u'bar'),
1230             (None, END, u'root')]
1231            )
1232
1233    def test_remove_attr(self):
1234        self.assertEqual(
1235            self._attr('foo', 'name', None),
1236            [(None, START, (u'root', {})),
1237             (None, TEXT, u'ROOT'),
1238             (ENTER, START, (u'foo', {u'size': '100'})),
1239             (INSIDE, TEXT, u'FOO'),
1240             (EXIT, END, u'foo'),
1241             (None, START, (u'bar', {u'name': u'bar'})),
1242             (None, TEXT, u'BAR'),
1243             (None, END, u'bar'),
1244             (None, END, u'root')]
1245            )
1246
1247    def test_remove_attr_with_function(self):
1248        def set(name, event):
1249            return None
1250
1251        self.assertEqual(
1252            self._attr('foo', 'name', set),
1253            [(None, START, (u'root', {})),
1254             (None, TEXT, u'ROOT'),
1255             (ENTER, START, (u'foo', {u'size': '100'})),
1256             (INSIDE, TEXT, u'FOO'),
1257             (EXIT, END, u'foo'),
1258             (None, START, (u'bar', {u'name': u'bar'})),
1259             (None, TEXT, u'BAR'),
1260             (None, END, u'bar'),
1261             (None, END, u'root')]
1262            )
1263
1264
1265class BufferTestMixin(object):
1266    def _apply(self, select, with_attrs=False):
1267        buffer = StreamBuffer()
1268        events = buffer.events
1269
1270        class Trace(object):
1271            last = None
1272            trace = []
1273
1274            def __call__(self, stream):
1275                for event in stream:
1276                    if events and hash(tuple(events)) != self.last:
1277                        self.last = hash(tuple(events))
1278                        self.trace.append(list(events))
1279                    yield event
1280
1281        trace = Trace()
1282        output = _transform(FOOBAR, getattr(Transformer(select), self.operation)
1283                                    (buffer).apply(trace), with_attrs=with_attrs)
1284        simplified = []
1285        for interval in trace.trace:
1286            simplified.append(_simplify([(None, e) for e in interval],
1287                                         with_attrs=with_attrs))
1288        return output, simplified
1289
1290
1291class CopyTest(unittest.TestCase, BufferTestMixin):
1292    operation = 'copy'
1293
1294    def test_copy_element(self):
1295        self.assertEqual(
1296            self._apply('foo')[1],
1297            [[(None, START, u'foo'),
1298              (None, TEXT, u'FOO'),
1299              (None, END, u'foo')]]
1300            )
1301
1302    def test_copy_adjacent_elements(self):
1303        self.assertEqual(
1304            self._apply('foo|bar')[1],
1305            [[(None, START, u'foo'),
1306              (None, TEXT, u'FOO'),
1307              (None, END, u'foo')],
1308             [(None, START, u'bar'),
1309              (None, TEXT, u'BAR'),
1310              (None, END, u'bar')]]
1311            )
1312
1313    def test_copy_all(self):
1314        self.assertEqual(
1315            self._apply('*|text()')[1],
1316            [[(None, TEXT, u'ROOT')],
1317             [(None, START, u'foo'),
1318              (None, TEXT, u'FOO'),
1319              (None, END, u'foo')],
1320             [(None, START, u'bar'),
1321              (None, TEXT, u'BAR'),
1322              (None, END, u'bar')]]
1323            )
1324
1325    def test_copy_text(self):
1326        self.assertEqual(
1327            self._apply('*/text()')[1],
1328            [[(None, TEXT, u'FOO')],
1329             [(None, TEXT, u'BAR')]]
1330            )
1331
1332    def test_copy_context(self):
1333        self.assertEqual(
1334            self._apply('.')[1],
1335            [[(None, START, u'root'),
1336              (None, TEXT, u'ROOT'),
1337              (None, START, u'foo'),
1338              (None, TEXT, u'FOO'),
1339              (None, END, u'foo'),
1340              (None, START, u'bar'),
1341              (None, TEXT, u'BAR'),
1342              (None, END, u'bar'),
1343              (None, END, u'root')]]
1344            )
1345
1346    def test_copy_attribute(self):
1347        self.assertEqual(
1348            self._apply('foo/@name', with_attrs=True)[1],
1349            [[(None, ATTR, {'name': u'foo'})]]
1350            )
1351
1352    def test_copy_attributes(self):
1353        self.assertEqual(
1354            self._apply('foo/@*', with_attrs=True)[1],
1355            [[(None, ATTR, {u'name': u'foo', u'size': u'100'})]]
1356            )
1357
1358
1359class CutTest(unittest.TestCase, BufferTestMixin):
1360    operation = 'cut'
1361
1362    def test_cut_element(self):
1363        self.assertEqual(
1364            self._apply('foo'),
1365            ([(None, START, u'root'),
1366              (None, TEXT, u'ROOT'),
1367              (None, START, u'bar'),
1368              (None, TEXT, u'BAR'),
1369              (None, END, u'bar'),
1370              (None, END, u'root')],
1371             [[(None, START, u'foo'),
1372               (None, TEXT, u'FOO'),
1373               (None, END, u'foo')]])
1374            )
1375
1376    def test_cut_adjacent_elements(self):
1377        self.assertEqual(
1378            self._apply('foo|bar'),
1379            ([(None, START, u'root'), 
1380              (None, TEXT, u'ROOT'),
1381              (BREAK, BREAK, None),
1382              (None, END, u'root')],
1383             [[(None, START, u'foo'),
1384               (None, TEXT, u'FOO'),
1385               (None, END, u'foo')],
1386              [(None, START, u'bar'),
1387               (None, TEXT, u'BAR'),
1388               (None, END, u'bar')]])
1389            )
1390
1391    def test_cut_all(self):
1392        self.assertEqual(
1393            self._apply('*|text()'),
1394            ([(None, 'START', u'root'),
1395              ('BREAK', 'BREAK', None),
1396              ('BREAK', 'BREAK', None),
1397              (None, 'END', u'root')],
1398             [[(None, 'TEXT', u'ROOT')],
1399              [(None, 'START', u'foo'),
1400               (None, 'TEXT', u'FOO'),
1401               (None, 'END', u'foo')],
1402              [(None, 'START', u'bar'),
1403               (None, 'TEXT', u'BAR'),
1404               (None, 'END', u'bar')]])
1405            )
1406
1407    def test_cut_text(self):
1408        self.assertEqual(
1409            self._apply('*/text()'),
1410            ([(None, 'START', u'root'),
1411              (None, 'TEXT', u'ROOT'),
1412              (None, 'START', u'foo'),
1413              (None, 'END', u'foo'),
1414              (None, 'START', u'bar'),
1415              (None, 'END', u'bar'),
1416              (None, 'END', u'root')],
1417             [[(None, 'TEXT', u'FOO')],
1418              [(None, 'TEXT', u'BAR')]])
1419            )
1420
1421    def test_cut_context(self):
1422        self.assertEqual(
1423            self._apply('.')[1],
1424            [[(None, 'START', u'root'),
1425              (None, 'TEXT', u'ROOT'),
1426              (None, 'START', u'foo'),
1427              (None, 'TEXT', u'FOO'),
1428              (None, 'END', u'foo'),
1429              (None, 'START', u'bar'),
1430              (None, 'TEXT', u'BAR'),
1431              (None, 'END', u'bar'),
1432              (None, 'END', u'root')]]
1433            )
1434
1435    def test_cut_attribute(self):
1436        self.assertEqual(
1437            self._apply('foo/@name', with_attrs=True),
1438            ([(None, START, (u'root', {})),
1439              (None, TEXT, u'ROOT'),
1440              (None, START, (u'foo', {u'size': u'100'})),
1441              (None, TEXT, u'FOO'),
1442              (None, END, u'foo'),
1443              (None, START, (u'bar', {u'name': u'bar'})),
1444              (None, TEXT, u'BAR'),
1445              (None, END, u'bar'),
1446              (None, END, u'root')],
1447             [[(None, ATTR, {u'name': u'foo'})]])
1448            )
1449
1450    def test_cut_attributes(self):
1451        self.assertEqual(
1452            self._apply('foo/@*', with_attrs=True),
1453            ([(None, START, (u'root', {})),
1454              (None, TEXT, u'ROOT'),
1455              (None, START, (u'foo', {})),
1456              (None, TEXT, u'FOO'),
1457              (None, END, u'foo'),
1458              (None, START, (u'bar', {u'name': u'bar'})),
1459              (None, TEXT, u'BAR'),
1460              (None, END, u'bar'),
1461              (None, END, u'root')],
1462             [[(None, ATTR, {u'name': u'foo', u'size': u'100'})]])
1463            )
1464
1465# XXX Test this when the XPath implementation is fixed (#233).
1466#    def test_cut_attribute_or_attribute(self):
1467#        self.assertEqual(
1468#            self._apply('foo/@name | foo/@size', with_attrs=True),
1469#            ([(None, START, (u'root', {})),
1470#              (None, TEXT, u'ROOT'),
1471#              (None, START, (u'foo', {})),
1472#              (None, TEXT, u'FOO'),
1473#              (None, END, u'foo'),
1474#              (None, START, (u'bar', {u'name': u'bar'})),
1475#              (None, TEXT, u'BAR'),
1476#              (None, END, u'bar'),
1477#              (None, END, u'root')],
1478#             [[(None, ATTR, {u'name': u'foo', u'size': u'100'})]])
1479#            )
1480
1481
1482
1483
1484def suite():
1485    from genshi.input import HTML
1486    from genshi.core import Markup
1487    from genshi.builder import tag
1488    suite = unittest.TestSuite()
1489    for test in (SelectTest, InvertTest, EndTest,
1490                 EmptyTest, RemoveTest, UnwrapText, WrapTest, FilterTest,
1491                 MapTest, SubstituteTest, RenameTest, ReplaceTest, BeforeTest,
1492                 AfterTest, PrependTest, AppendTest, AttrTest, CopyTest, CutTest):
1493        suite.addTest(unittest.makeSuite(test, 'test'))
1494    suite.addTest(doctest.DocTestSuite(
1495        genshi.filters.transform, optionflags=doctest.NORMALIZE_WHITESPACE,
1496        extraglobs={'HTML': HTML, 'tag': tag, 'Markup': Markup}))
1497    return suite
1498
1499
1500if __name__ == '__main__':
1501    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.