1# Copyright (c) 2004-2009 Divmod.
2# See LICENSE for details.
3
4import os
5
6from twisted.trial import unittest, util
7
8from nevow import context
9from nevow import flat
10from nevow.flat.flatstan import _PrecompiledSlot
11from nevow import loaders
12from nevow import tags as t
13
14class TestDocFactories(unittest.TestCase):
15
16    def _preprocessorTest(self, docFactory):
17        def preprocessor(uncompiled):
18            self.assertEquals(len(uncompiled), 1)
19            uncompiled = uncompiled[0]
20            self.assertEquals(uncompiled.tagName, 'div')
21            self.assertEquals(len(uncompiled.children), 2)
22            self.assertEquals(uncompiled.children[0].tagName, 'span')
23            self.assertEquals(uncompiled.children[0].children, ['Hello'])
24            self.assertEquals(uncompiled.children[1].tagName, 'span')
25            self.assertEquals(uncompiled.children[1].children, ['world'])
26            return t.div['goodbye.']
27        doc = docFactory.load(preprocessors=[preprocessor])
28        self.assertEquals(doc, ['<div>goodbye.</div>'])
29
30
31    def test_stanPreprocessors(self):
32        """
33        Test that the stan loader properly passes uncompiled documents to
34        preprocessors it is given.
35        """
36        factory = loaders.stan(
37            t.div[t.span['Hello'], t.span['world']])
38        return self._preprocessorTest(factory)
39
40
41    def test_stan(self):
42        doc = t.ul(id='nav')[t.li['one'], t.li['two'], t.li['three']]
43        df = loaders.stan(doc)
44        self.assertEquals(df.load()[0], '<ul id="nav"><li>one</li><li>two</li><li>three</li></ul>')
45
46
47    def test_stanPrecompiled(self):
48        """
49        Test that a stan loader works with precompiled documents.
50
51        (This behavior will probably be deprecated soon, but we need to test
52        that it works right until we remove it.)
53        """
54        doc = flat.precompile(t.ul(id='nav')[t.li['one'], t.li['two'], t.slot('three')])
55        df = loaders.stan(doc)
56        loaded = df.load()
57        self.assertEqual(loaded[0], '<ul id="nav"><li>one</li><li>two</li>')
58        self.failUnless(isinstance(loaded[1], _PrecompiledSlot))
59        self.assertEqual(loaded[1].name, 'three')
60        self.assertEqual(loaded[2], '</ul>')
61
62
63    def test_htmlstr(self):
64        doc = '<ul id="nav"><li>a</li><li>b</li><li>c</li></ul>'
65        df = loaders.htmlstr(doc)
66        self.assertEquals(df.load()[0], doc)
67    test_htmlstr.suppress = [
68        util.suppress(message=
69                      r"\[v0.8\] htmlstr is deprecated because it's buggy. "
70                      "Please start using xmlfile and/or xmlstr.")]
71
72
73    def test_htmlfile(self):
74        doc = '<ul id="nav"><li>a</li><li>b</li><li>c</li></ul>'
75        temp = self.mktemp()
76        f = file(temp, 'w')
77        f.write(doc)
78        f.close()
79        df = loaders.htmlfile(temp)
80        self.assertEquals(df.load()[0], doc)
81    test_htmlfile.suppress = [
82        util.suppress(message=
83                      r"\[v0.8\] htmlfile is deprecated because it's buggy. "
84                      "Please start using xmlfile and/or xmlstr.")]
85
86
87    def test_htmlfile_slots(self):
88        doc = '<nevow:slot name="foo">Hi there</nevow:slot>'
89        temp = self.mktemp()
90        f = file(temp, 'w')
91        f.write(doc)
92        f.close()
93        df = loaders.htmlfile(temp)
94        self.assertEquals(df.load()[0].children, ['Hi there'])
95    test_htmlfile_slots.suppress = [
96        util.suppress(message=
97                      r"\[v0.8\] htmlfile is deprecated because it's buggy. "
98                      "Please start using xmlfile and/or xmlstr.")]
99
100
101    def test_xmlstr(self):
102        doc = '<ul id="nav"><li>a</li><li>b</li><li>c</li></ul>'
103        df = loaders.xmlstr(doc)
104        self.assertEquals(df.load()[0], doc)
105
106
107    def test_xmlstrPreprocessors(self):
108        """
109        Test that the xmlstr loader properly passes uncompiled documents to
110        preprocessors it is given.
111        """
112        factory = loaders.xmlstr(
113            '<div><span>Hello</span><span>world</span></div>')
114        return self._preprocessorTest(factory)
115
116
117    def test_xmlfile(self):
118        doc = '<ul id="nav"><li>a</li><li>b</li><li>c</li></ul>'
119        temp = self.mktemp()
120        f = file(temp, 'w')
121        f.write(doc)
122        f.close()
123        df = loaders.xmlfile(temp)
124        self.assertEquals(df.load()[0], doc)
125
126
127    def test_xmlfilePreprocessors(self):
128        """
129        Test that the xmlstr loader properly passes uncompiled documents to
130        preprocessors it is given.
131        """
132        xmlFile = self.mktemp()
133        f = file(xmlFile, 'w')
134        f.write('<div><span>Hello</span><span>world</span></div>')
135        f.close()
136        factory = loaders.xmlfile(xmlFile)
137        return self._preprocessorTest(factory)
138
139
140    def test_patterned(self):
141        """Test fetching a specific part (a pattern) of the document.
142        """
143        doc = t.div[t.p[t.span(pattern='inner')['something']]]
144        df = loaders.stan(doc, pattern='inner')
145        self.assertEquals(df.load()[0].tagName, 'span')
146        self.assertEquals(df.load()[0].children[0], 'something')
147
148
149    def test_ignoreDocType(self):
150        doc = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<html><body><p>Hello.</p></body></html>'''
151        df = loaders.xmlstr(doc, ignoreDocType=True)
152        self.assertEquals(flat.flatten(df), '<html><body><p>Hello.</p></body></html>')
153
154    def test_ignoreComment(self):
155        doc = '<!-- skip this --><p>Hello.</p>'
156        df = loaders.xmlstr(doc, ignoreComment=True)
157        self.assertEquals(flat.flatten(df), '<p>Hello.</p>')
158
159
160class TestDocFactoriesCache(unittest.TestCase):
161
162    doc = '''
163    <div>
164    <p nevow:pattern="1">one</p>
165    <p nevow:pattern="2">two</p>
166    </div>
167    '''
168
169    nsdoc = '''
170    <div xmlns:nevow="http://nevow.com/ns/nevow/0.1">
171    <p nevow:pattern="1">one</p>
172    <p nevow:pattern="2">two</p>
173    </div>
174    '''
175
176    stan = t.div[t.p(pattern='1')['one'],t.p(pattern='2')['two']]
177
178    def test_stan(self):
179
180        loader = loaders.stan(self.stan)
181        self.assertEquals( id(loader.load()), id(loader.load()) )
182
183        loader = loaders.stan(self.stan, pattern='1')
184        self.assertEquals( id(loader.load()), id(loader.load()) )
185
186        l1 = loaders.stan(self.stan, pattern='1')
187        l2 = loaders.stan(self.stan, pattern='1')
188        self.assertNotEqual( id(l1.load()), id(l2.load()) )
189
190        l1 = loaders.stan(self.stan, pattern='1')
191        l2 = loaders.stan(self.stan, pattern='2')
192        self.assertNotEqual( id(l1.load()), id(l2.load()) )
193
194
195    def test_htmlstr(self):
196        loader = loaders.htmlstr(self.doc)
197        self.assertEquals( id(loader.load()), id(loader.load()) )
198
199        loader = loaders.htmlstr(self.doc, pattern='1')
200        self.assertEquals( id(loader.load()), id(loader.load()) )
201
202        l1 = loaders.htmlstr(self.doc, pattern='1')
203        l2 = loaders.htmlstr(self.doc, pattern='1')
204        self.assertNotEqual( id(l1.load()), id(l2.load()) )
205
206        l1 = loaders.htmlstr(self.doc, pattern='1')
207        l2 = loaders.htmlstr(self.doc, pattern='2')
208        self.assertNotEqual( id(l1.load()), id(l2.load()) )
209    test_htmlstr.suppress = [
210        util.suppress(message=
211                      r"\[v0.8\] htmlstr is deprecated because it's buggy. "
212                      "Please start using xmlfile and/or xmlstr.")]
213
214
215    def test_htmlfile(self):
216        temp = self.mktemp()
217        f = file(temp, 'w')
218        f.write(self.doc)
219        f.close()
220
221        loader = loaders.htmlfile(temp)
222        self.assertEquals( id(loader.load()), id(loader.load()) )
223
224        l1 = loaders.htmlfile(temp, pattern='1')
225        l2 = loaders.htmlfile(temp, pattern='1')
226        self.assertNotEqual( id(l1.load()), id(l2.load()) )
227
228        l1 = loaders.htmlfile(temp, pattern='1')
229        l2 = loaders.htmlfile(temp, pattern='2')
230        self.assertNotEqual( id(l1.load()), id(l2.load()) )
231    test_htmlfile.suppress = [
232        util.suppress(message=
233                      r"\[v0.8\] htmlfile is deprecated because it's buggy. "
234                      "Please start using xmlfile and/or xmlstr.")]
235
236
237    def test_htmlfileReload(self):
238        temp = self.mktemp()
239        f = file(temp, 'w')
240        f.write(self.doc)
241        f.close()
242
243        loader = loaders.htmlfile(temp)
244        r = loader.load()
245        self.assertEquals(id(r), id(loader.load()))
246        os.utime(temp, (os.path.getatime(temp), os.path.getmtime(temp)+5))
247        self.assertNotEqual(id(r), id(loader.load()))
248    test_htmlfileReload.suppress = [
249        util.suppress(message=
250                      r"\[v0.8\] htmlfile is deprecated because it's buggy. "
251                      "Please start using xmlfile and/or xmlstr.")]
252
253
254
255    def test_xmlstr(self):
256
257        loader = loaders.xmlstr(self.nsdoc)
258        self.assertEquals( id(loader.load()), id(loader.load()) )
259
260        loader = loaders.xmlstr(self.nsdoc, pattern='1')
261        self.assertEquals( id(loader.load()), id(loader.load()) )
262
263        l1 = loaders.xmlstr(self.nsdoc, pattern='1')
264        l2 = loaders.xmlstr(self.nsdoc, pattern='1')
265        self.assertNotEqual( id(l1.load()), id(l2.load()) )
266
267        l1 = loaders.xmlstr(self.nsdoc, pattern='1')
268        l2 = loaders.xmlstr(self.nsdoc, pattern='2')
269        self.assertNotEqual( id(l1.load()), id(l2.load()) )
270
271
272    def test_xmlSlotDefault(self):
273        """
274        An I{nevow:slot} tag in an XML template may have a I{default}
275        attribute specifying a value for the slot if it is not otherwise
276        given one.
277        """
278        slotsdoc = '''
279        <div xmlns:nevow="http://nevow.com/ns/nevow/0.1">
280        <nevow:slot name="1" />
281        <nevow:slot name="2" default="3" />
282        </div>
283        '''
284        loader = loaders.xmlstr(slotsdoc)
285        loaded = loader.load()
286        self.assertEquals(loaded[1].default, None)
287        self.assertEquals(loaded[3].default, "3")
288
289
290    def test_xmlfile(self):
291
292        temp = self.mktemp()
293        f = file(temp, 'w')
294        f.write(self.nsdoc)
295        f.close()
296
297        loader = loaders.xmlfile(temp)
298        self.assertEquals( id(loader.load()), id(loader.load()) )
299
300        loader = loaders.xmlfile(temp, pattern='1')
301        self.assertEquals( id(loader.load()), id(loader.load()) )
302
303        l1 = loaders.xmlfile(temp, pattern='1')
304        l2 = loaders.xmlfile(temp, pattern='1')
305        self.assertNotEqual( id(l1.load()), id(l2.load()) )
306
307        l1 = loaders.xmlfile(temp, pattern='1')
308        l2 = loaders.xmlfile(temp, pattern='2')
309        self.assertNotEqual( id(l1.load()), id(l2.load()) )
310
311    def test_xmlfileReload(self):
312
313        temp = self.mktemp()
314        f = file(temp, 'w')
315        f.write(self.nsdoc)
316        f.close()
317
318        loader = loaders.xmlfile(temp)
319        r = loader.load()
320        self.assertEquals(id(r), id(loader.load()))
321        os.utime(temp, (os.path.getatime(temp), os.path.getmtime(temp)+5))
322        self.assertNotEqual(id(r), id(loader.load()))
323
324    def test_reloadAfterPrecompile(self):
325        """
326        """
327        # Get a filename
328        temp = self.mktemp()
329
330        # Write some content
331        f = file(temp, 'w')
332        f.write('<p>foo</p>')
333        f.close()
334
335        # Precompile the doc
336        ctx = context.WovenContext()
337        doc = loaders.htmlfile(temp)
338        pc = flat.precompile(flat.flatten(doc), ctx)
339
340        before = ''.join(flat.serialize(pc, ctx))
341
342
343        # Write the file with different content and make sure the
344        # timestamp changes
345        f = file(temp, 'w')
346        f.write('<p>bar</p>')
347        f.close()
348        os.utime(temp, (os.path.getatime(temp), os.path.getmtime(temp)+5))
349
350        after = ''.join(flat.serialize(pc, ctx))
351
352        self.assertIn('foo', before)
353        self.assertIn('bar', after)
354        self.failIfEqual(before, after)
355    test_reloadAfterPrecompile.todo = \
356        'Fix so that disk templates are reloaded even after a precompile. ' \
357        'Probably just a matter of making the DocSerializer really lazy'
358
359
360class TestContext(unittest.TestCase):
361    """Check that each of the standard loaders supports load with and without a
362    context.
363    """
364
365    def test_stan(self):
366        doc = t.p['hello']
367        self._withAndWithout(loaders.stan(doc))
368
369    def test_xmlstr(self):
370        doc = '<p>hello</p>'
371        self._withAndWithout(loaders.xmlstr(doc))
372
373    def test_xmlfile(self):
374        temp = self.mktemp()
375        f = file(temp, 'w')
376        f.write('<p>hello</p>')
377        f.close()
378        self._withAndWithout(loaders.xmlfile(temp))
379
380    def test_htmlstr(self):
381        doc = '<p>hello</p>'
382        self._withAndWithout(loaders.htmlstr(doc))
383    test_htmlstr.suppress = [
384        util.suppress(message=
385                      r"\[v0.8\] htmlstr is deprecated because it's buggy. "
386                      "Please start using xmlfile and/or xmlstr.")]
387
388    def test_htmlfile(self):
389        temp = self.mktemp()
390        f = file(temp, 'w')
391        f.write('<p>hello</p>')
392        f.close()
393        self._withAndWithout(loaders.htmlfile(temp))
394    test_htmlfile.suppress = [
395        util.suppress(message=
396                      r"\[v0.8\] htmlfile is deprecated because it's buggy. "
397                      "Please start using xmlfile and/or xmlstr.")]
398
399    def _withAndWithout(self, loader):
400        ctx = context.WovenContext()
401        self.assertEquals(loader.load(), ['<p>hello</p>'])
402        self.assertEquals(loader.load(ctx), ['<p>hello</p>'])
403
404
405class TestParsing(unittest.TestCase):
406
407    def test_missingSpace(self):
408        doc = '<p xmlns:nevow="http://nevow.com/ns/nevow/0.1"><nevow:slot name="foo"/> <nevow:slot name="foo"/></p>'
409        ## This used to say htmlstr, and this test failed because microdom ignores whitespace;
410        ## This test passes fine using xmlstr. I am not going to fix this because microdom is too
411        ## hard to fix. If you need this, switch to xmlstr.
412        result = loaders.xmlstr(doc).load()
413        # There should be a space between the two slots
414        self.assertEquals(result[2], ' ')
415