Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / test / test_memcache.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 """
5 Test the memcache client protocol.
6 """
7
8 from twisted.internet.error import ConnectionDone
9
10 from twisted.protocols.memcache import MemCacheProtocol, NoSuchCommand
11 from twisted.protocols.memcache import ClientError, ServerError
12
13 from twisted.trial.unittest import TestCase
14 from twisted.test.proto_helpers import StringTransportWithDisconnection
15 from twisted.internet.task import Clock
16 from twisted.internet.defer import Deferred, gatherResults, TimeoutError
17 from twisted.internet.defer import DeferredList
18
19
20
21 class CommandMixin:
22     """
23     Setup and tests for basic invocation of L{MemCacheProtocol} commands.
24     """
25
26     def _test(self, d, send, recv, result):
27         """
28         Helper test method to test the resulting C{Deferred} of a
29         L{MemCacheProtocol} command.
30         """
31         raise NotImplementedError()
32
33
34     def test_get(self):
35         """
36         L{MemCacheProtocol.get} returns a L{Deferred} which is called back with
37         the value and the flag associated with the given key if the server
38         returns a successful result.
39         """
40         return self._test(self.proto.get("foo"), "get foo\r\n",
41             "VALUE foo 0 3\r\nbar\r\nEND\r\n", (0, "bar"))
42
43
44     def test_emptyGet(self):
45         """
46         Test getting a non-available key: it succeeds but return C{None} as
47         value and C{0} as flag.
48         """
49         return self._test(self.proto.get("foo"), "get foo\r\n",
50             "END\r\n", (0, None))
51
52
53     def test_getMultiple(self):
54         """
55         L{MemCacheProtocol.getMultiple} returns a L{Deferred} which is called
56         back with a dictionary of flag, value for each given key.
57         """
58         return self._test(self.proto.getMultiple(['foo', 'cow']),
59             "get foo cow\r\n",
60             "VALUE foo 0 3\r\nbar\r\nVALUE cow 0 7\r\nchicken\r\nEND\r\n",
61             {'cow': (0, 'chicken'), 'foo': (0, 'bar')})
62
63
64     def test_getMultipleWithEmpty(self):
65         """
66         When L{MemCacheProtocol.getMultiple} is called with non-available keys,
67         the corresponding tuples are (0, None).
68         """
69         return self._test(self.proto.getMultiple(['foo', 'cow']),
70             "get foo cow\r\n",
71             "VALUE cow 1 3\r\nbar\r\nEND\r\n",
72             {'cow': (1, 'bar'), 'foo': (0, None)})
73
74
75     def test_set(self):
76         """
77         L{MemCacheProtocol.set} returns a L{Deferred} which is called back with
78         C{True} when the operation succeeds.
79         """
80         return self._test(self.proto.set("foo", "bar"),
81             "set foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
82
83
84     def test_add(self):
85         """
86         L{MemCacheProtocol.add} returns a L{Deferred} which is called back with
87         C{True} when the operation succeeds.
88         """
89         return self._test(self.proto.add("foo", "bar"),
90             "add foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
91
92
93     def test_replace(self):
94         """
95         L{MemCacheProtocol.replace} returns a L{Deferred} which is called back
96         with C{True} when the operation succeeds.
97         """
98         return self._test(self.proto.replace("foo", "bar"),
99             "replace foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
100
101
102     def test_errorAdd(self):
103         """
104         Test an erroneous add: if a L{MemCacheProtocol.add} is called but the
105         key already exists on the server, it returns a B{NOT STORED} answer,
106         which calls back the resulting L{Deferred} with C{False}.
107         """
108         return self._test(self.proto.add("foo", "bar"),
109             "add foo 0 0 3\r\nbar\r\n", "NOT STORED\r\n", False)
110
111
112     def test_errorReplace(self):
113         """
114         Test an erroneous replace: if a L{MemCacheProtocol.replace} is called
115         but the key doesn't exist on the server, it returns a B{NOT STORED}
116         answer, which calls back the resulting L{Deferred} with C{False}.
117         """
118         return self._test(self.proto.replace("foo", "bar"),
119             "replace foo 0 0 3\r\nbar\r\n", "NOT STORED\r\n", False)
120
121
122     def test_delete(self):
123         """
124         L{MemCacheProtocol.delete} returns a L{Deferred} which is called back
125         with C{True} when the server notifies a success.
126         """
127         return self._test(self.proto.delete("bar"), "delete bar\r\n",
128             "DELETED\r\n", True)
129
130
131     def test_errorDelete(self):
132         """
133         Test a error during a delete: if key doesn't exist on the server, it
134         returns a B{NOT FOUND} answer which calls back the resulting L{Deferred}
135         with C{False}.
136         """
137         return self._test(self.proto.delete("bar"), "delete bar\r\n",
138             "NOT FOUND\r\n", False)
139
140
141     def test_increment(self):
142         """
143         Test incrementing a variable: L{MemCacheProtocol.increment} returns a
144         L{Deferred} which is called back with the incremented value of the
145         given key.
146         """
147         return self._test(self.proto.increment("foo"), "incr foo 1\r\n",
148             "4\r\n", 4)
149
150
151     def test_decrement(self):
152         """
153         Test decrementing a variable: L{MemCacheProtocol.decrement} returns a
154         L{Deferred} which is called back with the decremented value of the
155         given key.
156         """
157         return self._test(
158             self.proto.decrement("foo"), "decr foo 1\r\n", "5\r\n", 5)
159
160
161     def test_incrementVal(self):
162         """
163         L{MemCacheProtocol.increment} takes an optional argument C{value} which
164         replaces the default value of 1 when specified.
165         """
166         return self._test(self.proto.increment("foo", 8), "incr foo 8\r\n",
167             "4\r\n", 4)
168
169
170     def test_decrementVal(self):
171         """
172         L{MemCacheProtocol.decrement} takes an optional argument C{value} which
173         replaces the default value of 1 when specified.
174         """
175         return self._test(self.proto.decrement("foo", 3), "decr foo 3\r\n",
176             "5\r\n", 5)
177
178
179     def test_stats(self):
180         """
181         Test retrieving server statistics via the L{MemCacheProtocol.stats}
182         command: it parses the data sent by the server and calls back the
183         resulting L{Deferred} with a dictionary of the received statistics.
184         """
185         return self._test(self.proto.stats(), "stats\r\n",
186             "STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
187             {"foo": "bar", "egg": "spam"})
188
189
190     def test_statsWithArgument(self):
191         """
192         L{MemCacheProtocol.stats} takes an optional C{str} argument which,
193         if specified, is sent along with the I{STAT} command.  The I{STAT}
194         responses from the server are parsed as key/value pairs and returned
195         as a C{dict} (as in the case where the argument is not specified).
196         """
197         return self._test(self.proto.stats("blah"), "stats blah\r\n",
198             "STAT foo bar\r\nSTAT egg spam\r\nEND\r\n",
199             {"foo": "bar", "egg": "spam"})
200
201
202     def test_version(self):
203         """
204         Test version retrieval via the L{MemCacheProtocol.version} command: it
205         returns a L{Deferred} which is called back with the version sent by the
206         server.
207         """
208         return self._test(self.proto.version(), "version\r\n",
209             "VERSION 1.1\r\n", "1.1")
210
211
212     def test_flushAll(self):
213         """
214         L{MemCacheProtocol.flushAll} returns a L{Deferred} which is called back
215         with C{True} if the server acknowledges success.
216         """
217         return self._test(self.proto.flushAll(), "flush_all\r\n",
218             "OK\r\n", True)
219
220
221
222 class MemCacheTestCase(CommandMixin, TestCase):
223     """
224     Test client protocol class L{MemCacheProtocol}.
225     """
226
227     def setUp(self):
228         """
229         Create a memcache client, connect it to a string protocol, and make it
230         use a deterministic clock.
231         """
232         self.proto = MemCacheProtocol()
233         self.clock = Clock()
234         self.proto.callLater = self.clock.callLater
235         self.transport = StringTransportWithDisconnection()
236         self.transport.protocol = self.proto
237         self.proto.makeConnection(self.transport)
238
239
240     def _test(self, d, send, recv, result):
241         """
242         Implementation of C{_test} which checks that the command sends C{send}
243         data, and that upon reception of C{recv} the result is C{result}.
244
245         @param d: the resulting deferred from the memcache command.
246         @type d: C{Deferred}
247
248         @param send: the expected data to be sent.
249         @type send: C{str}
250
251         @param recv: the data to simulate as reception.
252         @type recv: C{str}
253
254         @param result: the expected result.
255         @type result: C{any}
256         """
257         def cb(res):
258             self.assertEqual(res, result)
259         self.assertEqual(self.transport.value(), send)
260         d.addCallback(cb)
261         self.proto.dataReceived(recv)
262         return d
263
264
265     def test_invalidGetResponse(self):
266         """
267         If the value returned doesn't match the expected key of the current
268         C{get} command, an error is raised in L{MemCacheProtocol.dataReceived}.
269         """
270         self.proto.get("foo")
271         s = "spamegg"
272         self.assertRaises(RuntimeError,
273             self.proto.dataReceived,
274             "VALUE bar 0 %s\r\n%s\r\nEND\r\n" % (len(s), s))
275
276
277     def test_invalidMultipleGetResponse(self):
278         """
279         If the value returned doesn't match one the expected keys of the
280         current multiple C{get} command, an error is raised error in
281         L{MemCacheProtocol.dataReceived}.
282         """
283         self.proto.getMultiple(["foo", "bar"])
284         s = "spamegg"
285         self.assertRaises(RuntimeError,
286             self.proto.dataReceived,
287             "VALUE egg 0 %s\r\n%s\r\nEND\r\n" % (len(s), s))
288
289
290     def test_timeOut(self):
291         """
292         Test the timeout on outgoing requests: when timeout is detected, all
293         current commands fail with a L{TimeoutError}, and the connection is
294         closed.
295         """
296         d1 = self.proto.get("foo")
297         d2 = self.proto.get("bar")
298         d3 = Deferred()
299         self.proto.connectionLost = d3.callback
300
301         self.clock.advance(self.proto.persistentTimeOut)
302         self.assertFailure(d1, TimeoutError)
303         self.assertFailure(d2, TimeoutError)
304         def checkMessage(error):
305             self.assertEqual(str(error), "Connection timeout")
306         d1.addCallback(checkMessage)
307         return gatherResults([d1, d2, d3])
308
309
310     def test_timeoutRemoved(self):
311         """
312         When a request gets a response, no pending timeout call remains around.
313         """
314         d = self.proto.get("foo")
315
316         self.clock.advance(self.proto.persistentTimeOut - 1)
317         self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
318
319         def check(result):
320             self.assertEqual(result, (0, "bar"))
321             self.assertEqual(len(self.clock.calls), 0)
322         d.addCallback(check)
323         return d
324
325
326     def test_timeOutRaw(self):
327         """
328         Test the timeout when raw mode was started: the timeout is not reset
329         until all the data has been received, so we can have a L{TimeoutError}
330         when waiting for raw data.
331         """
332         d1 = self.proto.get("foo")
333         d2 = Deferred()
334         self.proto.connectionLost = d2.callback
335
336         self.proto.dataReceived("VALUE foo 0 10\r\n12345")
337         self.clock.advance(self.proto.persistentTimeOut)
338         self.assertFailure(d1, TimeoutError)
339         return gatherResults([d1, d2])
340
341
342     def test_timeOutStat(self):
343         """
344         Test the timeout when stat command has started: the timeout is not
345         reset until the final B{END} is received.
346         """
347         d1 = self.proto.stats()
348         d2 = Deferred()
349         self.proto.connectionLost = d2.callback
350
351         self.proto.dataReceived("STAT foo bar\r\n")
352         self.clock.advance(self.proto.persistentTimeOut)
353         self.assertFailure(d1, TimeoutError)
354         return gatherResults([d1, d2])
355
356
357     def test_timeoutPipelining(self):
358         """
359         When two requests are sent, a timeout call remains around for the
360         second request, and its timeout time is correct.
361         """
362         d1 = self.proto.get("foo")
363         d2 = self.proto.get("bar")
364         d3 = Deferred()
365         self.proto.connectionLost = d3.callback
366
367         self.clock.advance(self.proto.persistentTimeOut - 1)
368         self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n")
369
370         def check(result):
371             self.assertEqual(result, (0, "bar"))
372             self.assertEqual(len(self.clock.calls), 1)
373             for i in range(self.proto.persistentTimeOut):
374                 self.clock.advance(1)
375             return self.assertFailure(d2, TimeoutError).addCallback(checkTime)
376         def checkTime(ignored):
377             # Check that the timeout happened C{self.proto.persistentTimeOut}
378             # after the last response
379             self.assertEqual(
380                 self.clock.seconds(), 2 * self.proto.persistentTimeOut - 1)
381         d1.addCallback(check)
382         return d1
383
384
385     def test_timeoutNotReset(self):
386         """
387         Check that timeout is not resetted for every command, but keep the
388         timeout from the first command without response.
389         """
390         d1 = self.proto.get("foo")
391         d3 = Deferred()
392         self.proto.connectionLost = d3.callback
393
394         self.clock.advance(self.proto.persistentTimeOut - 1)
395         d2 = self.proto.get("bar")
396         self.clock.advance(1)
397         self.assertFailure(d1, TimeoutError)
398         self.assertFailure(d2, TimeoutError)
399         return gatherResults([d1, d2, d3])
400
401
402     def test_timeoutCleanDeferreds(self):
403         """
404         C{timeoutConnection} cleans the list of commands that it fires with
405         C{TimeoutError}: C{connectionLost} doesn't try to fire them again, but
406         sets the disconnected state so that future commands fail with a
407         C{RuntimeError}.
408         """
409         d1 = self.proto.get("foo")
410         self.clock.advance(self.proto.persistentTimeOut)
411         self.assertFailure(d1, TimeoutError)
412         d2 = self.proto.get("bar")
413         self.assertFailure(d2, RuntimeError)
414         return gatherResults([d1, d2])
415
416
417     def test_connectionLost(self):
418         """
419         When disconnection occurs while commands are still outstanding, the
420         commands fail.
421         """
422         d1 = self.proto.get("foo")
423         d2 = self.proto.get("bar")
424         self.transport.loseConnection()
425         done = DeferredList([d1, d2], consumeErrors=True)
426         def checkFailures(results):
427             for success, result in results:
428                 self.assertFalse(success)
429                 result.trap(ConnectionDone)
430         return done.addCallback(checkFailures)
431
432
433     def test_tooLongKey(self):
434         """
435         An error is raised when trying to use a too long key: the called
436         command returns a L{Deferred} which fails with a L{ClientError}.
437         """
438         d1 = self.assertFailure(self.proto.set("a" * 500, "bar"), ClientError)
439         d2 = self.assertFailure(self.proto.increment("a" * 500), ClientError)
440         d3 = self.assertFailure(self.proto.get("a" * 500), ClientError)
441         d4 = self.assertFailure(
442             self.proto.append("a" * 500, "bar"), ClientError)
443         d5 = self.assertFailure(
444             self.proto.prepend("a" * 500, "bar"), ClientError)
445         d6 = self.assertFailure(
446             self.proto.getMultiple(["foo", "a" * 500]), ClientError)
447         return gatherResults([d1, d2, d3, d4, d5, d6])
448
449
450     def test_invalidCommand(self):
451         """
452         When an unknown command is sent directly (not through public API), the
453         server answers with an B{ERROR} token, and the command fails with
454         L{NoSuchCommand}.
455         """
456         d = self.proto._set("egg", "foo", "bar", 0, 0, "")
457         self.assertEqual(self.transport.value(), "egg foo 0 0 3\r\nbar\r\n")
458         self.assertFailure(d, NoSuchCommand)
459         self.proto.dataReceived("ERROR\r\n")
460         return d
461
462
463     def test_clientError(self):
464         """
465         Test the L{ClientError} error: when the server sends a B{CLIENT_ERROR}
466         token, the originating command fails with L{ClientError}, and the error
467         contains the text sent by the server.
468         """
469         a = "eggspamm"
470         d = self.proto.set("foo", a)
471         self.assertEqual(self.transport.value(),
472                           "set foo 0 0 8\r\neggspamm\r\n")
473         self.assertFailure(d, ClientError)
474         def check(err):
475             self.assertEqual(str(err), "We don't like egg and spam")
476         d.addCallback(check)
477         self.proto.dataReceived("CLIENT_ERROR We don't like egg and spam\r\n")
478         return d
479
480
481     def test_serverError(self):
482         """
483         Test the L{ServerError} error: when the server sends a B{SERVER_ERROR}
484         token, the originating command fails with L{ServerError}, and the error
485         contains the text sent by the server.
486         """
487         a = "eggspamm"
488         d = self.proto.set("foo", a)
489         self.assertEqual(self.transport.value(),
490                           "set foo 0 0 8\r\neggspamm\r\n")
491         self.assertFailure(d, ServerError)
492         def check(err):
493             self.assertEqual(str(err), "zomg")
494         d.addCallback(check)
495         self.proto.dataReceived("SERVER_ERROR zomg\r\n")
496         return d
497
498
499     def test_unicodeKey(self):
500         """
501         Using a non-string key as argument to commands raises an error.
502         """
503         d1 = self.assertFailure(self.proto.set(u"foo", "bar"), ClientError)
504         d2 = self.assertFailure(self.proto.increment(u"egg"), ClientError)
505         d3 = self.assertFailure(self.proto.get(1), ClientError)
506         d4 = self.assertFailure(self.proto.delete(u"bar"), ClientError)
507         d5 = self.assertFailure(self.proto.append(u"foo", "bar"), ClientError)
508         d6 = self.assertFailure(self.proto.prepend(u"foo", "bar"), ClientError)
509         d7 = self.assertFailure(
510             self.proto.getMultiple(["egg", 1]), ClientError)
511         return gatherResults([d1, d2, d3, d4, d5, d6, d7])
512
513
514     def test_unicodeValue(self):
515         """
516         Using a non-string value raises an error.
517         """
518         return self.assertFailure(self.proto.set("foo", u"bar"), ClientError)
519
520
521     def test_pipelining(self):
522         """
523         Multiple requests can be sent subsequently to the server, and the
524         protocol orders the responses correctly and dispatch to the
525         corresponding client command.
526         """
527         d1 = self.proto.get("foo")
528         d1.addCallback(self.assertEqual, (0, "bar"))
529         d2 = self.proto.set("bar", "spamspamspam")
530         d2.addCallback(self.assertEqual, True)
531         d3 = self.proto.get("egg")
532         d3.addCallback(self.assertEqual, (0, "spam"))
533         self.assertEqual(self.transport.value(),
534             "get foo\r\nset bar 0 0 12\r\nspamspamspam\r\nget egg\r\n")
535         self.proto.dataReceived("VALUE foo 0 3\r\nbar\r\nEND\r\n"
536                                 "STORED\r\n"
537                                 "VALUE egg 0 4\r\nspam\r\nEND\r\n")
538         return gatherResults([d1, d2, d3])
539
540
541     def test_getInChunks(self):
542         """
543         If the value retrieved by a C{get} arrive in chunks, the protocol
544         is able to reconstruct it and to produce the good value.
545         """
546         d = self.proto.get("foo")
547         d.addCallback(self.assertEqual, (0, "0123456789"))
548         self.assertEqual(self.transport.value(), "get foo\r\n")
549         self.proto.dataReceived("VALUE foo 0 10\r\n0123456")
550         self.proto.dataReceived("789")
551         self.proto.dataReceived("\r\nEND")
552         self.proto.dataReceived("\r\n")
553         return d
554
555
556     def test_append(self):
557         """
558         L{MemCacheProtocol.append} behaves like a L{MemCacheProtocol.set}
559         method: it returns a L{Deferred} which is called back with C{True} when
560         the operation succeeds.
561         """
562         return self._test(self.proto.append("foo", "bar"),
563             "append foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
564
565
566     def test_prepend(self):
567         """
568         L{MemCacheProtocol.prepend} behaves like a L{MemCacheProtocol.set}
569         method: it returns a L{Deferred} which is called back with C{True} when
570         the operation succeeds.
571         """
572         return self._test(self.proto.prepend("foo", "bar"),
573             "prepend foo 0 0 3\r\nbar\r\n", "STORED\r\n", True)
574
575
576     def test_gets(self):
577         """
578         L{MemCacheProtocol.get} handles an additional cas result when
579         C{withIdentifier} is C{True} and forward it in the resulting
580         L{Deferred}.
581         """
582         return self._test(self.proto.get("foo", True), "gets foo\r\n",
583             "VALUE foo 0 3 1234\r\nbar\r\nEND\r\n", (0, "1234", "bar"))
584
585
586     def test_emptyGets(self):
587         """
588         Test getting a non-available key with gets: it succeeds but return
589         C{None} as value, C{0} as flag and an empty cas value.
590         """
591         return self._test(self.proto.get("foo", True), "gets foo\r\n",
592             "END\r\n", (0, "", None))
593
594
595     def test_getsMultiple(self):
596         """
597         L{MemCacheProtocol.getMultiple} handles an additional cas field in the
598         returned tuples if C{withIdentifier} is C{True}.
599         """
600         return self._test(self.proto.getMultiple(["foo", "bar"], True),
601             "gets foo bar\r\n",
602             "VALUE foo 0 3 1234\r\negg\r\nVALUE bar 0 4 2345\r\nspam\r\nEND\r\n",
603             {'bar': (0, '2345', 'spam'), 'foo': (0, '1234', 'egg')})
604
605
606     def test_getsMultipleWithEmpty(self):
607         """
608         When getting a non-available key with L{MemCacheProtocol.getMultiple}
609         when C{withIdentifier} is C{True}, the other keys are retrieved
610         correctly, and the non-available key gets a tuple of C{0} as flag,
611         C{None} as value, and an empty cas value.
612         """
613         return self._test(self.proto.getMultiple(["foo", "bar"], True),
614             "gets foo bar\r\n",
615             "VALUE foo 0 3 1234\r\negg\r\nEND\r\n",
616             {'bar': (0, '', None), 'foo': (0, '1234', 'egg')})
617
618
619     def test_checkAndSet(self):
620         """
621         L{MemCacheProtocol.checkAndSet} passes an additional cas identifier
622         that the server handles to check if the data has to be updated.
623         """
624         return self._test(self.proto.checkAndSet("foo", "bar", cas="1234"),
625             "cas foo 0 0 3 1234\r\nbar\r\n", "STORED\r\n", True)
626
627
628     def test_casUnknowKey(self):
629         """
630         When L{MemCacheProtocol.checkAndSet} response is C{EXISTS}, the
631         resulting L{Deferred} fires with C{False}.
632         """
633         return self._test(self.proto.checkAndSet("foo", "bar", cas="1234"),
634             "cas foo 0 0 3 1234\r\nbar\r\n", "EXISTS\r\n", False)
635
636
637
638 class CommandFailureTests(CommandMixin, TestCase):
639     """
640     Tests for correct failure of commands on a disconnected
641     L{MemCacheProtocol}.
642     """
643
644     def setUp(self):
645         """
646         Create a disconnected memcache client, using a deterministic clock.
647         """
648         self.proto = MemCacheProtocol()
649         self.clock = Clock()
650         self.proto.callLater = self.clock.callLater
651         self.transport = StringTransportWithDisconnection()
652         self.transport.protocol = self.proto
653         self.proto.makeConnection(self.transport)
654         self.transport.loseConnection()
655
656
657     def _test(self, d, send, recv, result):
658         """
659         Implementation of C{_test} which checks that the command fails with
660         C{RuntimeError} because the transport is disconnected. All the
661         parameters except C{d} are ignored.
662         """
663         return self.assertFailure(d, RuntimeError)