~divmod-dev/divmod.org/trunk

« back to all changes in this revision

Viewing changes to Epsilon/epsilon/test/test_juice.py

  • Committer: Jean-Paul Calderone
  • Date: 2014-06-29 20:33:04 UTC
  • mfrom: (2749.1.1 remove-epsilon-1325289)
  • Revision ID: exarkun@twistedmatrix.com-20140629203304-gdkmbwl1suei4m97
mergeĀ lp:~exarkun/divmod.org/remove-epsilon-1325289

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2005 Divmod, Inc.  See LICENSE file for details
2
 
 
3
 
 
4
 
from epsilon import juice
5
 
from epsilon.test import iosim
6
 
from twisted.trial import unittest
7
 
from twisted.internet import protocol, defer
8
 
 
9
 
class TestProto(protocol.Protocol):
10
 
    def __init__(self, onConnLost, dataToSend):
11
 
        self.onConnLost = onConnLost
12
 
        self.dataToSend = dataToSend
13
 
 
14
 
    def connectionMade(self):
15
 
        self.data = []
16
 
        self.transport.write(self.dataToSend)
17
 
 
18
 
    def dataReceived(self, bytes):
19
 
        self.data.append(bytes)
20
 
        self.transport.loseConnection()
21
 
 
22
 
    def connectionLost(self, reason):
23
 
        self.onConnLost.callback(self.data)
24
 
 
25
 
class SimpleSymmetricProtocol(juice.Juice):
26
 
 
27
 
    def sendHello(self, text):
28
 
        return self.sendCommand("hello",
29
 
                                hello=text)
30
 
 
31
 
    def sendGoodbye(self):
32
 
        return self.sendCommand("goodbye")
33
 
 
34
 
    def juice_HELLO(self, box):
35
 
        return juice.Box(hello=box['hello'])
36
 
 
37
 
    def juice_GOODBYE(self, box):
38
 
        return juice.QuitBox(goodbye='world')
39
 
 
40
 
class UnfriendlyGreeting(Exception):
41
 
    """Greeting was insufficiently kind.
42
 
    """
43
 
 
44
 
class UnknownProtocol(Exception):
45
 
    """Asked to switch to the wrong protocol.
46
 
    """
47
 
 
48
 
class Hello(juice.Command):
49
 
    commandName = 'hello'
50
 
    arguments = [('hello', juice.String())]
51
 
    response = [('hello', juice.String())]
52
 
 
53
 
    errors = {UnfriendlyGreeting: 'UNFRIENDLY'}
54
 
 
55
 
class Goodbye(juice.Command):
56
 
    commandName = 'goodbye'
57
 
    responseType = juice.QuitBox
58
 
 
59
 
class GetList(juice.Command):
60
 
    commandName = 'getlist'
61
 
    arguments = [('length', juice.Integer())]
62
 
    response = [('body', juice.JuiceList([('x', juice.Integer())]))]
63
 
 
64
 
class TestSwitchProto(juice.ProtocolSwitchCommand):
65
 
    commandName = 'Switch-Proto'
66
 
 
67
 
    arguments = [
68
 
        ('name', juice.String()),
69
 
        ]
70
 
    errors = {UnknownProtocol: 'UNKNOWN'}
71
 
 
72
 
class SingleUseFactory(protocol.ClientFactory):
73
 
    def __init__(self, proto):
74
 
        self.proto = proto
75
 
 
76
 
    def buildProtocol(self, addr):
77
 
        p, self.proto = self.proto, None
78
 
        return p
79
 
 
80
 
class SimpleSymmetricCommandProtocol(juice.Juice):
81
 
    maybeLater = None
82
 
    def __init__(self, issueGreeting, onConnLost=None):
83
 
        juice.Juice.__init__(self, issueGreeting)
84
 
        self.onConnLost = onConnLost
85
 
 
86
 
    def sendHello(self, text):
87
 
        return Hello(hello=text).do(self)
88
 
    def sendGoodbye(self):
89
 
        return Goodbye().do(self)
90
 
    def command_HELLO(self, hello):
91
 
        if hello.startswith('fuck'):
92
 
            raise UnfriendlyGreeting("Don't be a dick.")
93
 
        return dict(hello=hello)
94
 
    def command_GETLIST(self, length):
95
 
        return {'body': [dict(x=1)] * length}
96
 
    def command_GOODBYE(self):
97
 
        return dict(goodbye='world')
98
 
    command_HELLO.command = Hello
99
 
    command_GOODBYE.command = Goodbye
100
 
    command_GETLIST.command = GetList
101
 
 
102
 
    def switchToTestProtocol(self):
103
 
        p = TestProto(self.onConnLost, SWITCH_CLIENT_DATA)
104
 
        return TestSwitchProto(SingleUseFactory(p), name='test-proto').do(self).addCallback(lambda ign: p)
105
 
 
106
 
    def command_SWITCH_PROTO(self, name):
107
 
        if name == 'test-proto':
108
 
            return TestProto(self.onConnLost, SWITCH_SERVER_DATA)
109
 
        raise UnknownProtocol(name)
110
 
 
111
 
    command_SWITCH_PROTO.command = TestSwitchProto
112
 
 
113
 
class DeferredSymmetricCommandProtocol(SimpleSymmetricCommandProtocol):
114
 
    def command_SWITCH_PROTO(self, name):
115
 
        if name == 'test-proto':
116
 
            self.maybeLaterProto = TestProto(self.onConnLost, SWITCH_SERVER_DATA)
117
 
            self.maybeLater = defer.Deferred()
118
 
            return self.maybeLater
119
 
        raise UnknownProtocol(name)
120
 
 
121
 
    command_SWITCH_PROTO.command = TestSwitchProto
122
 
 
123
 
 
124
 
class SSPF: protocol = SimpleSymmetricProtocol
125
 
class SSSF(SSPF, protocol.ServerFactory): pass
126
 
class SSCF(SSPF, protocol.ClientFactory): pass
127
 
 
128
 
def connectedServerAndClient(ServerClass=lambda: SimpleSymmetricProtocol(True),
129
 
                             ClientClass=lambda: SimpleSymmetricProtocol(False),
130
 
                             *a, **kw):
131
 
    """Returns a 3-tuple: (client, server, pump)
132
 
    """
133
 
    return iosim.connectedServerAndClient(
134
 
        ServerClass, ClientClass,
135
 
        *a, **kw)
136
 
 
137
 
class TotallyDumbProtocol(protocol.Protocol):
138
 
    buf = ''
139
 
    def dataReceived(self, data):
140
 
        self.buf += data
141
 
 
142
 
class LiteralJuice(juice.Juice):
143
 
    def __init__(self, issueGreeting):
144
 
        juice.Juice.__init__(self, issueGreeting)
145
 
        self.boxes = []
146
 
 
147
 
    def juiceBoxReceived(self, box):
148
 
        self.boxes.append(box)
149
 
        return
150
 
 
151
 
class LiteralParsingTest(unittest.TestCase):
152
 
    def testBasicRequestResponse(self):
153
 
        c, s, p = connectedServerAndClient(ClientClass=TotallyDumbProtocol)
154
 
        HELLO = 'abcdefg'
155
 
        ASKTOK = 'hand-crafted-ask'
156
 
        c.transport.write(("""-Command: HeLlO
157
 
-Ask: %s
158
 
Hello: %s
159
 
World: this header is ignored
160
 
 
161
 
""" % (ASKTOK, HELLO,)).replace('\n','\r\n'))
162
 
        p.flush()
163
 
        asserts = {'hello': HELLO,
164
 
                   '-answer': ASKTOK}
165
 
        hdrs = [j.split(': ') for j in c.buf.split('\r\n')[:-2]]
166
 
        self.assertEquals(len(asserts), len(hdrs))
167
 
        for hdr in hdrs:
168
 
            k, v = hdr
169
 
            self.assertEquals(v, asserts[k.lower()])
170
 
 
171
 
    def testParsingRoundTrip(self):
172
 
        c, s, p = connectedServerAndClient(ClientClass=lambda: LiteralJuice(False),
173
 
                                           ServerClass=lambda: LiteralJuice(True))
174
 
 
175
 
        SIMPLE = ('simple', 'test')
176
 
        CE = ('ceq', ': ')
177
 
        CR = ('crtest', 'test\r')
178
 
        LF = ('lftest', 'hello\n')
179
 
        NEWLINE = ('newline', 'test\r\none\r\ntwo')
180
 
        NEWLINE2 = ('newline2', 'test\r\none\r\n two')
181
 
        BLANKLINE = ('newline3', 'test\r\n\r\nblank\r\n\r\nline')
182
 
        BODYTEST = (juice.BODY, 'blah\r\n\r\ntesttest')
183
 
 
184
 
        testData = [
185
 
            [SIMPLE],
186
 
            [SIMPLE, BODYTEST],
187
 
            [SIMPLE, CE],
188
 
            [SIMPLE, CR],
189
 
            [SIMPLE, CE, CR, LF],
190
 
            [CE, CR, LF],
191
 
            [SIMPLE, NEWLINE, CE, NEWLINE2],
192
 
            [BODYTEST, SIMPLE, NEWLINE]
193
 
            ]
194
 
 
195
 
        for test in testData:
196
 
            jb = juice.Box()
197
 
            jb.update(dict(test))
198
 
            jb.sendTo(c)
199
 
            p.flush()
200
 
            self.assertEquals(s.boxes[-1], jb)
201
 
 
202
 
SWITCH_CLIENT_DATA = 'Success!'
203
 
SWITCH_SERVER_DATA = 'No, really.  Success.'
204
 
 
205
 
class AppLevelTest(unittest.TestCase):
206
 
    def testHelloWorld(self):
207
 
        c, s, p = connectedServerAndClient()
208
 
        L = []
209
 
        HELLO = 'world'
210
 
        c.sendHello(HELLO).addCallback(L.append)
211
 
        p.flush()
212
 
        self.assertEquals(L[0]['hello'], HELLO)
213
 
 
214
 
    def testHelloWorldCommand(self):
215
 
        c, s, p = connectedServerAndClient(
216
 
            ServerClass=lambda: SimpleSymmetricCommandProtocol(True),
217
 
            ClientClass=lambda: SimpleSymmetricCommandProtocol(False))
218
 
        L = []
219
 
        HELLO = 'world'
220
 
        c.sendHello(HELLO).addCallback(L.append)
221
 
        p.flush()
222
 
        self.assertEquals(L[0]['hello'], HELLO)
223
 
 
224
 
    def testHelloErrorHandling(self):
225
 
        L=[]
226
 
        c, s, p = connectedServerAndClient(ServerClass=lambda: SimpleSymmetricCommandProtocol(True),
227
 
                                           ClientClass=lambda: SimpleSymmetricCommandProtocol(False))
228
 
        HELLO = 'fuck you'
229
 
        c.sendHello(HELLO).addErrback(L.append)
230
 
        p.flush()
231
 
        L[0].trap(UnfriendlyGreeting)
232
 
        self.assertEquals(str(L[0].value), "Don't be a dick.")
233
 
 
234
 
    def testJuiceListCommand(self):
235
 
        c, s, p = connectedServerAndClient(ServerClass=lambda: SimpleSymmetricCommandProtocol(True),
236
 
                                           ClientClass=lambda: SimpleSymmetricCommandProtocol(False))
237
 
        L = []
238
 
        GetList(length=10).do(c).addCallback(L.append)
239
 
        p.flush()
240
 
        values = L.pop().get('body')
241
 
        self.assertEquals(values, [{'x': 1}] * 10)
242
 
 
243
 
    def testFailEarlyOnArgSending(self):
244
 
        okayCommand = Hello(Hello="What?")
245
 
        self.assertRaises(RuntimeError, Hello)
246
 
 
247
 
    def testSupportsVersion1(self):
248
 
        c, s, p = connectedServerAndClient(ServerClass=lambda: juice.Juice(True),
249
 
                                           ClientClass=lambda: juice.Juice(False))
250
 
        negotiatedVersion = []
251
 
        s.renegotiateVersion(1).addCallback(negotiatedVersion.append)
252
 
        p.flush()
253
 
        self.assertEquals(negotiatedVersion[0], 1)
254
 
        self.assertEquals(c.protocolVersion, 1)
255
 
        self.assertEquals(s.protocolVersion, 1)
256
 
 
257
 
    def testProtocolSwitch(self, switcher=SimpleSymmetricCommandProtocol):
258
 
        self.testSucceeded = False
259
 
 
260
 
        serverDeferred = defer.Deferred()
261
 
        serverProto = switcher(True, serverDeferred)
262
 
        clientDeferred = defer.Deferred()
263
 
        clientProto = switcher(False, clientDeferred)
264
 
        c, s, p = connectedServerAndClient(ServerClass=lambda: serverProto,
265
 
                                           ClientClass=lambda: clientProto)
266
 
 
267
 
        switchDeferred = c.switchToTestProtocol()
268
 
 
269
 
        def cbConnsLost(((serverSuccess, serverData), (clientSuccess, clientData))):
270
 
            self.failUnless(serverSuccess)
271
 
            self.failUnless(clientSuccess)
272
 
            self.assertEquals(''.join(serverData), SWITCH_CLIENT_DATA)
273
 
            self.assertEquals(''.join(clientData), SWITCH_SERVER_DATA)
274
 
            self.testSucceeded = True
275
 
 
276
 
        def cbSwitch(proto):
277
 
            return defer.DeferredList([serverDeferred, clientDeferred]).addCallback(cbConnsLost)
278
 
 
279
 
        switchDeferred.addCallback(cbSwitch)
280
 
        p.flush()
281
 
        if serverProto.maybeLater is not None:
282
 
            serverProto.maybeLater.callback(serverProto.maybeLaterProto)
283
 
            p.flush()
284
 
        self.failUnless(self.testSucceeded)
285
 
 
286
 
    def testProtocolSwitchDeferred(self):
287
 
        return self.testProtocolSwitch(switcher=DeferredSymmetricCommandProtocol)