1# Copyright (c) 2004 Divmod.
2# See LICENSE for details.
3
4
5from twisted.internet import defer
6
7from nevow import context, inevow
8from nevow import testutil
9from nevow.flat import twist
10from nevow.util import Deferred
11
12from nevow import rend, loaders, tags
13
14
15
16def deferit(data):
17    return data.d
18
19
20def deferdot(data):
21    return data.d2
22
23
24class RenderHelper(testutil.TestCase):
25    def renderIt(self):
26        req = testutil.FakeRequest()
27        self.r.renderHTTP(context.PageContext(tag=self.r, parent=context.RequestContext(tag=req)))
28        return req
29
30
31class LaterRenderTest(RenderHelper):
32    def setUp(self):
33        self.d = Deferred()
34        self.d2 = Deferred()
35        self.r = rend.Page(
36            docFactory=loaders.stan(
37                tags.html(data=self)[
38                    'Hello ', tags.invisible[tags.invisible[tags.invisible[tags.invisible[deferit]]]],
39                    deferdot,
40                    ]
41                )
42            )
43
44    def test_deferredSupport(self):
45        req = self.renderIt()
46        self.assertEquals(req.v, '<html>Hello ')
47        self.d.callback("world")
48        self.assertEquals(req.v, '<html>Hello world')
49        self.d2.callback(".")
50        self.assertEquals(req.v, '<html>Hello world.</html>')
51
52
53    def test_deferredSupport2(self):
54        req = self.renderIt()
55        self.assertEquals(req.v, '<html>Hello ')
56        self.d2.callback(".")
57        self.assertEquals(req.v, '<html>Hello ')
58        self.d.callback("world")
59        self.assertEquals(req.v, '<html>Hello world.</html>')
60
61    def test_deferredSupport3(self):
62        self.r.buffered = True
63        req = self.renderIt()
64        self.assertEquals(req.v, '')
65        self.d.callback("world")
66        self.assertEquals(req.v, '')
67        self.d2.callback(".")
68        self.assertEquals(req.v, '<html>Hello world.</html>')
69
70    def test_renderNestedDeferredCallables(self):
71        """
72        Test flattening of a renderer which returns a Deferred which fires with
73        a renderer which returns a Deferred.
74        """
75        def render_inner(ctx, data):
76            return defer.succeed('')
77
78        def render_outer(ctx, data):
79            return defer.succeed(render_inner)
80
81        ctx = context.WovenContext()
82        ctx.remember(None, inevow.IData)
83
84        out = []
85        d = twist.deferflatten(render_outer, ctx, out.append)
86        def flattened(ign):
87            self.assertEquals(out, [''])
88        d.addCallback(flattened)
89        return d
90
91
92    def test_renderNestedDeferredErrorHandling(self):
93        """
94        Test that flattening a renderer which returns a Deferred which fires
95        with a renderer which raises an exception causes the outermost Deferred
96        to errback.
97        """
98        class NestedException(Exception):
99            pass
100
101        def render_inner(ctx, data):
102            raise NestedException()
103
104        def render_outer(ctx, data):
105            return defer.succeed(render_inner)
106
107        ctx = context.WovenContext()
108        ctx.remember(None, inevow.IData)
109
110        out = []
111        d = twist.deferflatten(render_outer, ctx, out.append)
112        return self.assertFailure(d, NestedException)
113
114
115class LaterDataTest(RenderHelper):
116    def data_later(self, context, data):
117        return self.d
118
119    def data_later2(self, context, data):
120        return self.d2
121
122    def setUp(self):
123        self.d = Deferred()
124        self.d2 = Deferred()
125        self.r = rend.Page(docFactory=loaders.stan(
126            tags.html(data=self.data_later)[
127                'Hello ', str, ' and '
128                'goodbye ',str,
129                tags.span(data=self.data_later2, render=str)]))
130
131    def test_deferredSupport(self):
132        req = self.renderIt()
133        self.assertEquals(req.v, '')
134        self.d.callback("world")
135        self.assertEquals(req.v, '<html>Hello world and goodbye world')
136        self.d2.callback(".")
137        self.assertEquals(req.v, '<html>Hello world and goodbye world.</html>')
138
139
140class SuperLaterDataTest(RenderHelper):
141    def test_reusedDeferredSupport(self):
142        """
143        Two occurrences of a particular slot are each replaced with the
144        result of the Deferred which is used to fill that slot.
145        """
146        doc = tags.html[
147            tags.slot('foo'), tags.slot('foo')]
148        doc.fillSlots('foo', defer.succeed(tags.span['Foo!!!']))
149        self.r = rend.Page(docFactory=loaders.stan(doc))
150        req = self.renderIt()
151        self.assertEquals(req.v, '<html><span>Foo!!!</span><span>Foo!!!</span></html>')
152
153
154    def test_rendererCalledOnce(self):
155        """
156        Make sure that if a Deferred fires with a render function that the
157        render function is called only once.
158        """
159        calls = []
160        def recorder(ctx, data):
161            calls.append(None)
162            return str(len(calls))
163        doc = tags.html[tags.directive('renderer')]
164        class RendererPage(rend.Page):
165            docFactory = loaders.stan(doc)
166            def render_renderer(self, ctx, data):
167                return defer.succeed(recorder)
168        self.r = RendererPage()
169        req = self.renderIt()
170        self.assertEquals(req.v, '<html>1</html>')
171