Edgewall Software

source: trunk/genshi/template/tests/loader.py

Last change on this file was 1193, checked in by hodgestar, 11 years ago

Add a test that demonstrates that absolute includes work (see #465).

  • Property svn:eol-style set to native
File size: 18.3 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2006-2010 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
15import os
16import shutil
17import tempfile
18import unittest
19
20from genshi.core import TEXT
21from genshi.template.loader import TemplateLoader
22from genshi.template.markup import MarkupTemplate
23
24
25class TemplateLoaderTestCase(unittest.TestCase):
26    """Tests for the template loader."""
27
28    def setUp(self):
29        self.dirname = tempfile.mkdtemp(suffix='markup_test')
30
31    def tearDown(self):
32        shutil.rmtree(self.dirname)
33
34    def test_search_path_empty(self):
35        loader = TemplateLoader()
36        self.assertEqual([], loader.search_path)
37
38    def test_search_path_as_string(self):
39        loader = TemplateLoader(self.dirname)
40        self.assertEqual([self.dirname], loader.search_path)
41
42    def test_relative_include_samedir(self):
43        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
44        try:
45            file1.write("""<div>Included</div>""")
46        finally:
47            file1.close()
48
49        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
50        try:
51            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
52              <xi:include href="tmpl1.html" />
53            </html>""")
54        finally:
55            file2.close()
56
57        loader = TemplateLoader([self.dirname])
58        tmpl = loader.load('tmpl2.html')
59        self.assertEqual("""<html>
60              <div>Included</div>
61            </html>""", tmpl.generate().render(encoding=None))
62
63    def test_relative_include_subdir(self):
64        os.mkdir(os.path.join(self.dirname, 'sub'))
65        file1 = open(os.path.join(self.dirname, 'sub', 'tmpl1.html'), 'w')
66        try:
67            file1.write("""<div>Included</div>""")
68        finally:
69            file1.close()
70
71        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
72        try:
73            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
74              <xi:include href="sub/tmpl1.html" />
75            </html>""")
76        finally:
77            file2.close()
78
79        loader = TemplateLoader([self.dirname])
80        tmpl = loader.load('tmpl2.html')
81        self.assertEqual("""<html>
82              <div>Included</div>
83            </html>""", tmpl.generate().render(encoding=None))
84
85    def test_relative_include_parentdir(self):
86        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
87        try:
88            file1.write("""<div>Included</div>""")
89        finally:
90            file1.close()
91
92        os.mkdir(os.path.join(self.dirname, 'sub'))
93        file2 = open(os.path.join(self.dirname, 'sub', 'tmpl2.html'), 'w')
94        try:
95            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
96              <xi:include href="../tmpl1.html" />
97            </html>""")
98        finally:
99            file2.close()
100
101        loader = TemplateLoader([self.dirname])
102        tmpl = loader.load('sub/tmpl2.html')
103        self.assertEqual("""<html>
104              <div>Included</div>
105            </html>""", tmpl.generate().render(encoding=None))
106
107    def test_relative_include_samesubdir(self):
108        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
109        try:
110            file1.write("""<div>Included tmpl1.html</div>""")
111        finally:
112            file1.close()
113
114        os.mkdir(os.path.join(self.dirname, 'sub'))
115        file2 = open(os.path.join(self.dirname, 'sub', 'tmpl1.html'), 'w')
116        try:
117            file2.write("""<div>Included sub/tmpl1.html</div>""")
118        finally:
119            file2.close()
120
121        file3 = open(os.path.join(self.dirname, 'sub', 'tmpl2.html'), 'w')
122        try:
123            file3.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
124              <xi:include href="tmpl1.html" />
125            </html>""")
126        finally:
127            file3.close()
128
129        loader = TemplateLoader([self.dirname])
130        tmpl = loader.load('sub/tmpl2.html')
131        self.assertEqual("""<html>
132              <div>Included sub/tmpl1.html</div>
133            </html>""", tmpl.generate().render(encoding=None))
134
135    def test_relative_include_without_search_path(self):
136        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
137        try:
138            file1.write("""<div>Included</div>""")
139        finally:
140            file1.close()
141
142        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
143        try:
144            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
145              <xi:include href="tmpl1.html" />
146            </html>""")
147        finally:
148            file2.close()
149
150        loader = TemplateLoader()
151        tmpl = loader.load(os.path.join(self.dirname, 'tmpl2.html'))
152        self.assertEqual("""<html>
153              <div>Included</div>
154            </html>""", tmpl.generate().render(encoding=None))
155
156    def test_relative_include_without_loader(self):
157        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
158        try:
159            file1.write("""<div>Included</div>""")
160        finally:
161            file1.close()
162
163        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
164        try:
165            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
166              <xi:include href="tmpl1.html" />
167            </html>""")
168        finally:
169            file2.close()
170
171        tmpl = MarkupTemplate("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
172              <xi:include href="tmpl1.html" />
173            </html>""", os.path.join(self.dirname, 'tmpl2.html'), 'tmpl2.html')
174        self.assertEqual("""<html>
175              <div>Included</div>
176            </html>""", tmpl.generate().render(encoding=None))
177
178    def test_relative_include_without_loader_relative(self):
179        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
180        try:
181            file1.write("""<div>Included</div>""")
182        finally:
183            file1.close()
184
185        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
186        try:
187            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
188              <xi:include href="tmpl1.html" />
189            </html>""")
190        finally:
191            file2.close()
192
193        tmpl = MarkupTemplate("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
194              <xi:include href="tmpl1.html" />
195            </html>""", filename=os.path.join(self.dirname, 'tmpl2.html'))
196        self.assertEqual("""<html>
197              <div>Included</div>
198            </html>""", tmpl.generate().render(encoding=None))
199
200    def test_relative_include_without_search_path_nested(self):
201        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
202        try:
203            file1.write("""<div>Included</div>""")
204        finally:
205            file1.close()
206
207        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
208        try:
209            file2.write("""<div xmlns:xi="http://www.w3.org/2001/XInclude">
210              <xi:include href="tmpl1.html" />
211            </div>""")
212        finally:
213            file2.close()
214
215        file3 = open(os.path.join(self.dirname, 'tmpl3.html'), 'w')
216        try:
217            file3.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
218              <xi:include href="tmpl2.html" />
219            </html>""")
220        finally:
221            file3.close()
222
223        loader = TemplateLoader()
224        tmpl = loader.load(os.path.join(self.dirname, 'tmpl3.html'))
225        self.assertEqual("""<html>
226              <div>
227              <div>Included</div>
228            </div>
229            </html>""", tmpl.generate().render(encoding=None))
230
231    def test_relative_include_from_inmemory_template(self):
232        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
233        try:
234            file1.write("""<div>Included</div>""")
235        finally:
236            file1.close()
237
238        loader = TemplateLoader([self.dirname])
239        tmpl2 = MarkupTemplate("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
240          <xi:include href="../tmpl1.html" />
241        </html>""", filename='subdir/tmpl2.html', loader=loader)
242
243        self.assertEqual("""<html>
244          <div>Included</div>
245        </html>""", tmpl2.generate().render(encoding=None))
246
247    def test_relative_absolute_template_preferred(self):
248        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
249        try:
250            file1.write("""<div>Included</div>""")
251        finally:
252            file1.close()
253
254        os.mkdir(os.path.join(self.dirname, 'sub'))
255        file2 = open(os.path.join(self.dirname, 'sub', 'tmpl1.html'), 'w')
256        try:
257            file2.write("""<div>Included from sub</div>""")
258        finally:
259            file2.close()
260
261        file3 = open(os.path.join(self.dirname, 'sub', 'tmpl2.html'), 'w')
262        try:
263            file3.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
264              <xi:include href="tmpl1.html" />
265            </html>""")
266        finally:
267            file3.close()
268
269        loader = TemplateLoader()
270        tmpl = loader.load(os.path.abspath(os.path.join(self.dirname, 'sub',
271                                                        'tmpl2.html')))
272        self.assertEqual("""<html>
273              <div>Included from sub</div>
274            </html>""", tmpl.generate().render(encoding=None))
275
276    def test_absolute_include(self):
277        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
278        try:
279            file1.write("""<div>Included</div>""")
280        finally:
281            file1.close()
282
283        os.mkdir(os.path.join(self.dirname, 'sub'))
284        file2 = open(os.path.join(self.dirname, 'sub', 'tmpl2.html'), 'w')
285        try:
286            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
287              <xi:include href="%s/tmpl1.html" />
288            </html>""" % self.dirname)
289        finally:
290            file2.close()
291
292        loader = TemplateLoader()
293        tmpl = loader.load(os.path.abspath(os.path.join(self.dirname, 'sub',
294                                                        'tmpl2.html')))
295        self.assertEqual("""<html>
296              <div>Included</div>
297            </html>""", tmpl.generate().render(encoding=None))
298
299    def test_abspath_caching(self):
300        abspath = os.path.join(self.dirname, 'abs')
301        os.mkdir(abspath)
302        file1 = open(os.path.join(abspath, 'tmpl1.html'), 'w')
303        try:
304            file1.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
305              <xi:include href="tmpl2.html" />
306            </html>""")
307        finally:
308            file1.close()
309
310        file2 = open(os.path.join(abspath, 'tmpl2.html'), 'w')
311        try:
312            file2.write("""<div>Included from abspath.</div>""")
313        finally:
314            file2.close()
315
316        searchpath = os.path.join(self.dirname, 'searchpath')
317        os.mkdir(searchpath)
318        file3 = open(os.path.join(searchpath, 'tmpl2.html'), 'w')
319        try:
320            file3.write("""<div>Included from searchpath.</div>""")
321        finally:
322            file3.close()
323
324        loader = TemplateLoader(searchpath)
325        tmpl1 = loader.load(os.path.join(abspath, 'tmpl1.html'))
326        self.assertEqual("""<html>
327              <div>Included from searchpath.</div>
328            </html>""", tmpl1.generate().render(encoding=None))
329        assert 'tmpl2.html' in loader._cache
330
331    def test_abspath_include_caching_without_search_path(self):
332        file1 = open(os.path.join(self.dirname, 'tmpl1.html'), 'w')
333        try:
334            file1.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
335              <xi:include href="tmpl2.html" />
336            </html>""")
337        finally:
338            file1.close()
339
340        file2 = open(os.path.join(self.dirname, 'tmpl2.html'), 'w')
341        try:
342            file2.write("""<div>Included</div>""")
343        finally:
344            file2.close()
345
346        os.mkdir(os.path.join(self.dirname, 'sub'))
347        file3 = open(os.path.join(self.dirname, 'sub', 'tmpl1.html'), 'w')
348        try:
349            file3.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
350              <xi:include href="tmpl2.html" />
351            </html>""")
352        finally:
353            file3.close()
354
355        file4 = open(os.path.join(self.dirname, 'sub', 'tmpl2.html'), 'w')
356        try:
357            file4.write("""<div>Included from sub</div>""")
358        finally:
359            file4.close()
360
361        loader = TemplateLoader()
362        tmpl1 = loader.load(os.path.join(self.dirname, 'tmpl1.html'))
363        self.assertEqual("""<html>
364              <div>Included</div>
365            </html>""", tmpl1.generate().render(encoding=None))
366        tmpl2 = loader.load(os.path.join(self.dirname, 'sub', 'tmpl1.html'))
367        self.assertEqual("""<html>
368              <div>Included from sub</div>
369            </html>""", tmpl2.generate().render(encoding=None))
370        assert 'tmpl2.html' not in loader._cache
371
372    def test_load_with_default_encoding(self):
373        f = open(os.path.join(self.dirname, 'tmpl.html'), 'wb')
374        try:
375            f.write(u'<div>\xf6</div>'.encode('iso-8859-1'))
376        finally:
377            f.close()
378        loader = TemplateLoader([self.dirname], default_encoding='iso-8859-1')
379        loader.load('tmpl.html')
380
381    def test_load_with_explicit_encoding(self):
382        f = open(os.path.join(self.dirname, 'tmpl.html'), 'wb')
383        try:
384            f.write(u'<div>\xf6</div>'.encode('iso-8859-1'))
385        finally:
386            f.close()
387        loader = TemplateLoader([self.dirname], default_encoding='utf-8')
388        loader.load('tmpl.html', encoding='iso-8859-1')
389
390    def test_load_with_callback(self):
391        fileobj = open(os.path.join(self.dirname, 'tmpl.html'), 'w')
392        try:
393            fileobj.write("""<html>
394              <p>Hello</p>
395            </html>""")
396        finally:
397            fileobj.close()
398
399        def template_loaded(template):
400            def my_filter(stream, ctxt):
401                for kind, data, pos in stream:
402                    if kind is TEXT and data.strip():
403                        data = ', '.join([data, data.lower()])
404                    yield kind, data, pos
405            template.filters.insert(0, my_filter)
406
407        loader = TemplateLoader([self.dirname], callback=template_loaded)
408        tmpl = loader.load('tmpl.html')
409        self.assertEqual("""<html>
410              <p>Hello, hello</p>
411            </html>""", tmpl.generate().render(encoding=None))
412
413        # Make sure the filter is only added once
414        tmpl = loader.load('tmpl.html')
415        self.assertEqual("""<html>
416              <p>Hello, hello</p>
417            </html>""", tmpl.generate().render(encoding=None))
418
419    def test_prefix_delegation_to_directories(self):
420        """
421        Test prefix delegation with the following layout:
422       
423        templates/foo.html
424        sub1/templates/tmpl1.html
425        sub2/templates/tmpl2.html
426       
427        Where sub1 and sub2 are prefixes, and both tmpl1.html and tmpl2.html
428        incldue foo.html.
429        """
430        dir1 = os.path.join(self.dirname, 'templates')
431        os.mkdir(dir1)
432        file1 = open(os.path.join(dir1, 'foo.html'), 'w')
433        try:
434            file1.write("""<div>Included foo</div>""")
435        finally:
436            file1.close()
437
438        dir2 = os.path.join(self.dirname, 'sub1', 'templates')
439        os.makedirs(dir2)
440        file2 = open(os.path.join(dir2, 'tmpl1.html'), 'w')
441        try:
442            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
443              <xi:include href="../foo.html" /> from sub1
444            </html>""")
445        finally:
446            file2.close()
447
448        dir3 = os.path.join(self.dirname, 'sub2', 'templates')
449        os.makedirs(dir3)
450        file3 = open(os.path.join(dir3, 'tmpl2.html'), 'w')
451        try:
452            file3.write("""<div>tmpl2</div>""")
453        finally:
454            file3.close()
455
456        loader = TemplateLoader([dir1, TemplateLoader.prefixed(
457            sub1 = dir2,
458            sub2 = dir3
459        )])
460        tmpl = loader.load('sub1/tmpl1.html')
461        self.assertEqual("""<html>
462              <div>Included foo</div> from sub1
463            </html>""", tmpl.generate().render(encoding=None))
464
465    def test_prefix_delegation_to_directories_with_subdirs(self):
466        """
467        Test prefix delegation with the following layout:
468       
469        templates/foo.html
470        sub1/templates/tmpl1.html
471        sub1/templates/tmpl2.html
472        sub1/templates/bar/tmpl3.html
473       
474        Where sub1 is a prefix, and tmpl1.html includes all the others.
475        """
476        dir1 = os.path.join(self.dirname, 'templates')
477        os.mkdir(dir1)
478        file1 = open(os.path.join(dir1, 'foo.html'), 'w')
479        try:
480            file1.write("""<div>Included foo</div>""")
481        finally:
482            file1.close()
483
484        dir2 = os.path.join(self.dirname, 'sub1', 'templates')
485        os.makedirs(dir2)
486        file2 = open(os.path.join(dir2, 'tmpl1.html'), 'w')
487        try:
488            file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
489              <xi:include href="../foo.html" /> from sub1
490              <xi:include href="tmpl2.html" /> from sub1
491              <xi:include href="bar/tmpl3.html" /> from sub1
492            </html>""")
493        finally:
494            file2.close()
495
496        file3 = open(os.path.join(dir2, 'tmpl2.html'), 'w')
497        try:
498            file3.write("""<div>tmpl2</div>""")
499        finally:
500            file3.close()
501
502        dir3 = os.path.join(self.dirname, 'sub1', 'templates', 'bar')
503        os.makedirs(dir3)
504        file4 = open(os.path.join(dir3, 'tmpl3.html'), 'w')
505        try:
506            file4.write("""<div>bar/tmpl3</div>""")
507        finally:
508            file4.close()
509
510        loader = TemplateLoader([dir1, TemplateLoader.prefixed(
511            sub1 = os.path.join(dir2),
512            sub2 = os.path.join(dir3)
513        )])
514        tmpl = loader.load('sub1/tmpl1.html')
515        self.assertEqual("""<html>
516              <div>Included foo</div> from sub1
517              <div>tmpl2</div> from sub1
518              <div>bar/tmpl3</div> from sub1
519            </html>""", tmpl.generate().render(encoding=None))
520
521
522def suite():
523    suite = unittest.TestSuite()
524    suite.addTest(doctest.DocTestSuite(TemplateLoader.__module__))
525    suite.addTest(unittest.makeSuite(TemplateLoaderTestCase, 'test'))
526    return suite
527
528if __name__ == '__main__':
529    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.