123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572 |
- # Copyright 2012 Pinterest.com
- # -*- coding: utf-8 -*-
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from builtins import bytes as newbytes
- import collections
- import errno
- import functools
- import json
- import os
- import mock
- import platform
- import re
- import socket
- import sys
- import unittest
- import pytest
- from library.pymemcache.client.base import (
- PooledClient,
- Client,
- normalize_server_spec,
- KeepaliveOpts,
- check_key_helper
- )
- from library.pymemcache.exceptions import (
- MemcacheClientError,
- MemcacheServerError,
- MemcacheUnexpectedCloseError,
- MemcacheUnknownCommandError,
- MemcacheUnknownError,
- MemcacheIllegalInputError
- )
- from library.pymemcache import pool
- from library.pymemcache.test.utils import MockMemcacheClient
- # TODO: Use ipaddress module when dropping support for Python < 3.3
- def is_ipv6(address):
- return re.match(r'^[0-9a-f:]+$', address)
- @pytest.mark.parametrize(
- 'key,allow_unicode_keys,key_prefix,ex_exception,ex_excinfo,ignore_py27',
- [
- (u'b'*251, True, b'',
- MemcacheIllegalInputError, 'Key is too long', False),
- (u'foo bar', True, b'',
- MemcacheIllegalInputError, 'Key contains whitespace', False),
- (u'\00', True, b'',
- MemcacheIllegalInputError, 'Key contains null', False),
- (None, True, b'', TypeError, None, False),
- # The following test won't fail with a TypeError with python 2.7
- (b"", False, '', TypeError, None, True),
- ])
- @pytest.mark.unit()
- def test_check_key_helper_failing_conditions(key, allow_unicode_keys,
- key_prefix, ex_exception,
- ex_excinfo, ignore_py27):
- if ignore_py27 and sys.version_info < (3, 0, 0):
- pytest.skip("skipping for Python 2.7")
- with pytest.raises(ex_exception) as excinfo:
- check_key_helper(key, allow_unicode_keys, key_prefix)
- # Allow to ignore the excinfo value. Different implementation (2.7,
- # pypy, 3.x) comes with various error messages for the same thing.
- # We just check the kind of the exception.
- if ex_excinfo:
- assert ex_excinfo in str(excinfo.value)
- @pytest.mark.unit()
- def test_check_key_helper():
- assert check_key_helper(b"key", True, b'') == b"key"
- assert check_key_helper("key", True) == b"key"
- assert isinstance(check_key_helper(b"key", True), bytes)
- assert check_key_helper("", True) == b""
- class MockSocket(object):
- def __init__(self, recv_bufs, connect_failure=None, close_failure=None):
- self.recv_bufs = collections.deque(recv_bufs)
- self.send_bufs = []
- self.closed = False
- self.timeouts = []
- self.connect_failure = connect_failure
- self.close_failure = close_failure
- self.connections = []
- self.socket_options = []
- @property
- def family(self):
- any_ipv6 = any(is_ipv6(c[0]) for c in self.connections)
- return socket.AF_INET6 if any_ipv6 else socket.AF_INET
- def sendall(self, value):
- self.send_bufs.append(value)
- def close(self):
- if isinstance(self.close_failure, Exception):
- raise self.close_failure
- self.closed = True
- def recv(self, size):
- value = self.recv_bufs.popleft()
- if isinstance(value, Exception):
- raise value
- return value
- def settimeout(self, timeout):
- self.timeouts.append(timeout)
- def connect(self, server):
- if isinstance(self.connect_failure, Exception):
- raise self.connect_failure
- self.connections.append(server)
- def setsockopt(self, level, option, value):
- self.socket_options.append((level, option, value))
- class MockUnixSocketServer(object):
- def __init__(self, socket_path):
- if os.path.exists(socket_path):
- os.remove(socket_path)
- self.socket_path = socket_path
- self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- def __enter__(self):
- self.socket.bind(self.socket_path)
- self.socket.listen(1)
- return self.socket
- def __exit__(self, *args):
- self.socket.close()
- os.remove(self.socket_path)
- class MockSocketModule(object):
- def __init__(self, connect_failure=None, close_failure=None):
- self.connect_failure = connect_failure
- self.close_failure = close_failure
- self.sockets = []
- def socket(self, family, type, proto=0, fileno=None):
- socket = MockSocket(
- [],
- connect_failure=self.connect_failure,
- close_failure=self.close_failure)
- self.sockets.append(socket)
- return socket
- def getaddrinfo(self, host, port, family=0, type=0, proto=0, flags=0):
- family = family or (
- socket.AF_INET6 if is_ipv6(host) else socket.AF_INET
- )
- type = type or socket.SOCK_STREAM
- proto = proto or socket.IPPROTO_TCP
- sockaddr = (
- ('::1', 11211, 0, 0)
- if family == socket.AF_INET6
- else ('127.0.0.1', 11211)
- )
- return [(family, type, proto, '', sockaddr)]
- def __getattr__(self, name):
- return getattr(socket, name)
- class CustomizedClient(Client):
- def _extract_value(self, expect_cas, line, buf, remapped_keys,
- prefixed_keys):
- return b'key', b'value', b'END\r\n'
- @pytest.mark.unit()
- class ClientTestMixin(object):
- def make_client(self, mock_socket_values, **kwargs):
- client = Client(None, **kwargs)
- # mock out client._connect() rather than hard-settting client.sock to
- # ensure methods are checking whether self.sock is None before
- # attempting to use it
- sock = MockSocket(list(mock_socket_values))
- client._connect = mock.Mock(side_effect=functools.partial(
- setattr, client, "sock", sock))
- return client
- def make_customized_client(self, mock_socket_values, **kwargs):
- client = CustomizedClient(None, **kwargs)
- # mock out client._connect() rather than hard-settting client.sock to
- # ensure methods are checking whether self.sock is None before
- # attempting to use it
- sock = MockSocket(list(mock_socket_values))
- client._connect = mock.Mock(side_effect=functools.partial(
- setattr, client, "sock", sock))
- return client
- def test_set_success(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.set(b'key', b'value', noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set(b'key', b'value', noreply=False)
- assert result is True
- # unit test for set operation with parameter flags
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set(b'key', b'value', noreply=False, flags=0x00000030)
- assert result is True
- def test_set_future(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.set(newbytes(b'key'), newbytes(b'value'), noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set(newbytes(b'key'), newbytes(b'value'), noreply=False)
- assert result is True
- # unit test for set operation with parameter flags
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set(newbytes(b'key'), newbytes(b'value'), noreply=False,
- flags=0x00000030)
- assert result is True
- def test_set_unicode_key(self):
- client = self.make_client([b''])
- def _set():
- client.set(u'\u0FFF', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_unicode_key_ok(self):
- client = self.make_client([b'STORED\r\n'], allow_unicode_keys=True)
- result = client.set(u'\u0FFF', b'value', noreply=False)
- assert result is True
- def test_set_unicode_key_ok_snowman(self):
- client = self.make_client([b'STORED\r\n'], allow_unicode_keys=True)
- result = client.set('my☃', b'value', noreply=False)
- assert result is True
- def test_set_unicode_char_in_middle_of_key(self):
- client = self.make_client([b'STORED\r\n'])
- def _set():
- client.set('helloworld_\xb1901520_%c3', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_unicode_char_in_middle_of_key_snowman(self):
- client = self.make_client([b'STORED\r\n'])
- def _set():
- client.set('my☃', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_unicode_value(self):
- client = self.make_client([b''])
- def _set():
- client.set(b'key', u'\u0FFF', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_unicode_char_in_middle_of_key_ok(self):
- client = self.make_client([b'STORED\r\n'], allow_unicode_keys=True)
- result = client.set('helloworld_\xb1901520_%c3', b'value',
- noreply=False)
- assert result is True
- def test_set_noreply(self):
- client = self.make_client([])
- result = client.set(b'key', b'value', noreply=True)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([], encoding='utf-8')
- result = client.set(b'key', b'value', noreply=True)
- assert result is True
- def test_set_many_success(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.set_many({b'key': b'value'}, noreply=False)
- assert result == []
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set_many({b'key': b'value'}, noreply=False)
- assert result == []
- def test_set_multi_success(self):
- # Should just map to set_many
- client = self.make_client([b'STORED\r\n'])
- result = client.set_multi({b'key': b'value'}, noreply=False)
- assert result == []
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set_multi({b'key': b'value'}, noreply=False)
- assert result == []
- def test_add_stored(self):
- client = self.make_client([b'STORED\r', b'\n'])
- result = client.add(b'key', b'value', noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r', b'\n'], encoding='utf-8')
- result = client.add(b'key', b'value', noreply=False)
- assert result is True
- def test_add_not_stored(self):
- client = self.make_client([b'STORED\r', b'\n',
- b'NOT_', b'STOR', b'ED', b'\r\n'])
- client.add(b'key', b'value', noreply=False)
- result = client.add(b'key', b'value', noreply=False)
- assert result is False
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r', b'\n',
- b'NOT_', b'STOR', b'ED', b'\r\n'],
- encoding='utf-8')
- client.add(b'key', b'value', noreply=False)
- result = client.add(b'key', b'value', noreply=False)
- assert result is False
- def test_get_not_found(self):
- client = self.make_client([b'END\r\n'])
- result = client.get(b'key')
- assert result is None
- # Unit test for customized client (override _extract_value)
- client = self.make_customized_client([b'END\r\n'])
- result = client.get(b'key')
- assert result is None
- def test_space_key(self):
- client = self.make_client([b''])
- with pytest.raises(MemcacheIllegalInputError):
- client.get(b'space key')
- with pytest.raises(MemcacheIllegalInputError):
- client.set(b'space key', b'value')
- def test_get_not_found_default(self):
- client = self.make_client([b'END\r\n'])
- result = client.get(b'key', default='foobar')
- assert result == 'foobar'
- # Unit test for customized client (override _extract_value)
- client = self.make_customized_client([b'END\r\n'])
- result = client.get(b'key', default='foobar')
- assert result == 'foobar'
- def test_get_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'VALUE key 0 5\r\nvalue\r\nEND\r\n',
- ])
- client.set(b'key', b'value', noreply=False)
- result = client.get(b'key')
- assert result == b'value'
- # Unit test for customized client (override _extract_value)
- client = self.make_customized_client([
- b'STORED\r\n',
- b'VALUE key 0 5\r\nvalue\r\nEND\r\n',
- ])
- client.set(b'key', b'value', noreply=False)
- result = client.get(b'key')
- assert result == b'value'
- def test_get_many_none_found(self):
- client = self.make_client([b'END\r\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {}
- def test_get_multi_none_found(self):
- client = self.make_client([b'END\r\n'])
- result = client.get_multi([b'key1', b'key2'])
- assert result == {}
- def test_get_many_some_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'VALUE key1 0 6\r\nvalue1\r\nEND\r\n',
- ])
- client.set(b'key1', b'value1', noreply=False)
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1'}
- def test_get_many_all_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'STORED\r\n',
- b'VALUE key1 0 6\r\nvalue1\r\n',
- b'VALUE key2 0 6\r\nvalue2\r\nEND\r\n',
- ])
- client.set(b'key1', b'value1', noreply=False)
- client.set(b'key2', b'value2', noreply=False)
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- def test_get_unicode_key(self):
- client = self.make_client([b''])
- def _get():
- client.get(u'\u0FFF')
- with pytest.raises(MemcacheIllegalInputError):
- _get()
- def test_delete_not_found(self):
- client = self.make_client([b'NOT_FOUND\r\n'])
- result = client.delete(b'key', noreply=False)
- assert result is False
- def test_delete_found(self):
- client = self.make_client([b'STORED\r', b'\n', b'DELETED\r\n'])
- client.add(b'key', b'value', noreply=False)
- result = client.delete(b'key', noreply=False)
- assert result is True
- def test_delete_noreply(self):
- client = self.make_client([])
- result = client.delete(b'key', noreply=True)
- assert result is True
- def test_delete_many_no_keys(self):
- client = self.make_client([])
- result = client.delete_many([], noreply=False)
- assert result is True
- def test_delete_many_none_found(self):
- client = self.make_client([b'NOT_FOUND\r\n'])
- result = client.delete_many([b'key'], noreply=False)
- assert result is True
- def test_delete_many_found(self):
- client = self.make_client([b'STORED\r', b'\n', b'DELETED\r\n'])
- client.add(b'key', b'value', noreply=False)
- result = client.delete_many([b'key'], noreply=False)
- assert result is True
- def test_delete_many_some_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'DELETED\r\n',
- b'NOT_FOUND\r\n'
- ])
- client.add(b'key', b'value', noreply=False)
- result = client.delete_many([b'key', b'key2'], noreply=False)
- assert result is True
- def test_delete_multi_some_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'DELETED\r\n',
- b'NOT_FOUND\r\n'
- ])
- client.add(b'key', b'value', noreply=False)
- result = client.delete_multi([b'key', b'key2'], noreply=False)
- assert result is True
- def test_incr_not_found(self):
- client = self.make_client([b'NOT_FOUND\r\n'])
- result = client.incr(b'key', 1, noreply=False)
- assert result is None
- def test_incr_found(self):
- client = self.make_client([b'STORED\r\n', b'1\r\n'])
- client.set(b'key', 0, noreply=False)
- result = client.incr(b'key', 1, noreply=False)
- assert result == 1
- def test_incr_noreply(self):
- client = self.make_client([b'STORED\r\n'])
- client.set(b'key', 0, noreply=False)
- client = self.make_client([])
- result = client.incr(b'key', 1, noreply=True)
- assert result is None
- def test_decr_not_found(self):
- client = self.make_client([b'NOT_FOUND\r\n'])
- result = client.decr(b'key', 1, noreply=False)
- assert result is None
- def test_decr_found(self):
- client = self.make_client([b'STORED\r\n', b'1\r\n'])
- client.set(b'key', 2, noreply=False)
- result = client.decr(b'key', 1, noreply=False)
- assert result == 1
- class TestClient(ClientTestMixin, unittest.TestCase):
- Client = Client
- def test_append_stored(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.append(b'key', b'value', noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.append(b'key', b'value', noreply=False)
- assert result is True
- def test_prepend_stored(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.prepend(b'key', b'value', noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.prepend(b'key', b'value', noreply=False)
- assert result is True
- def test_cas_malformed(self):
- client = self.make_client([b'STORED\r\n'])
- with pytest.raises(MemcacheIllegalInputError):
- client.cas(b'key', b'value', None, noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- client.cas(b'key', b'value', 'nonintegerstring', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- # even a space makes it a noninteger string
- client.cas(b'key', b'value', '123 ', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- # non-ASCII digit
- client.cas(b'key', b'value', u'⁰', noreply=False)
- def test_cas_stored(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.cas(b'key', b'value', b'123', noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.cas(b'key', b'value', b'123', noreply=False)
- assert result is True
- def test_cas_exists(self):
- client = self.make_client([b'EXISTS\r\n'])
- result = client.cas(b'key', b'value', b'123', noreply=False)
- assert result is False
- # unit test for encoding passed in __init__()
- client = self.make_client([b'EXISTS\r\n'], encoding='utf-8')
- result = client.cas(b'key', b'value', b'123', noreply=False)
- assert result is False
- def test_cas_not_found(self):
- client = self.make_client([b'NOT_FOUND\r\n'])
- result = client.cas(b'key', b'value', b'123', noreply=False)
- assert result is None
- # unit test for encoding passed in __init__()
- client = self.make_client([b'NOT_FOUND\r\n'], encoding='utf-8')
- result = client.cas(b'key', b'value', b'123', noreply=False)
- assert result is None
- def test_cr_nl_boundaries(self):
- client = self.make_client([b'VALUE key1 0 6\r',
- b'\nvalue1\r\n'
- b'VALUE key2 0 6\r\n',
- b'value2\r\n'
- b'END\r\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- client = self.make_client([b'VALUE key1 0 6\r\n',
- b'value1\r',
- b'\nVALUE key2 0 6\r\n',
- b'value2\r\n',
- b'END\r\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- client = self.make_client([b'VALUE key1 0 6\r\n',
- b'value1\r\n',
- b'VALUE key2 0 6\r',
- b'\nvalue2\r\n',
- b'END\r\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- client = self.make_client([b'VALUE key1 0 6\r\n',
- b'value1\r\n',
- b'VALUE key2 0 6\r\n',
- b'value2\r',
- b'\nEND\r\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- client = self.make_client([b'VALUE key1 0 6\r\n',
- b'value1\r\n',
- b'VALUE key2 0 6\r\n',
- b'value2\r\n',
- b'END\r',
- b'\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- client = self.make_client([b'VALUE key1 0 6\r',
- b'\nvalue1\r',
- b'\nVALUE key2 0 6\r',
- b'\nvalue2\r',
- b'\nEND\r',
- b'\n'])
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- def test_delete_exception(self):
- client = self.make_client([Exception('fail')])
- def _delete():
- client.delete(b'key', noreply=False)
- with pytest.raises(Exception):
- _delete()
- assert client.sock is None
- def test_flush_all(self):
- client = self.make_client([b'OK\r\n'])
- result = client.flush_all(noreply=False)
- assert result is True
- def test_incr_exception(self):
- client = self.make_client([Exception('fail')])
- def _incr():
- client.incr(b'key', 1)
- with pytest.raises(Exception):
- _incr()
- assert client.sock is None
- def test_get_error(self):
- client = self.make_client([b'ERROR\r\n'])
- def _get():
- client.get(b'key')
- with pytest.raises(MemcacheUnknownCommandError):
- _get()
- def test_get_recv_chunks(self):
- client = self.make_client([b'VALUE key', b' 0 5\r', b'\nvalue',
- b'\r\n', b'END', b'\r', b'\n'])
- result = client.get(b'key')
- assert result == b'value'
- def test_get_unknown_error(self):
- client = self.make_client([b'foobarbaz\r\n'])
- def _get():
- client.get(b'key')
- with pytest.raises(MemcacheUnknownError):
- _get()
- def test_gets_not_found(self):
- client = self.make_client([b'END\r\n'])
- result = client.gets(b'key')
- assert result == (None, None)
- def test_gets_not_found_defaults(self):
- client = self.make_client([b'END\r\n'])
- result = client.gets(b'key', default='foo', cas_default='bar')
- assert result == ('foo', 'bar')
- def test_gets_found(self):
- client = self.make_client([b'VALUE key 0 5 10\r\nvalue\r\nEND\r\n'])
- result = client.gets(b'key')
- assert result == (b'value', b'10')
- def test_gets_many_none_found(self):
- client = self.make_client([b'END\r\n'])
- result = client.gets_many([b'key1', b'key2'])
- assert result == {}
- def test_gets_many_some_found(self):
- client = self.make_client([b'VALUE key1 0 6 11\r\nvalue1\r\nEND\r\n'])
- result = client.gets_many([b'key1', b'key2'])
- assert result == {b'key1': (b'value1', b'11')}
- def test_touch_not_found(self):
- client = self.make_client([b'NOT_FOUND\r\n'])
- result = client.touch(b'key', noreply=False)
- assert result is False
- def test_touch_found(self):
- client = self.make_client([b'TOUCHED\r\n'])
- result = client.touch(b'key', noreply=False)
- assert result is True
- def test_quit(self):
- client = self.make_client([])
- result = client.quit()
- assert result is None
- assert client.sock is None
- def test_shutdown(self):
- client = self.make_client([MemcacheUnexpectedCloseError('shutdown')])
- result = client.shutdown()
- assert result is None
- def test_shutdown_disabled(self):
- def _shutdown():
- client = self.make_client([b'ERROR: shutdown not enabled\r\n'])
- client.shutdown()
- with pytest.raises(MemcacheUnknownCommandError):
- _shutdown()
- def test_replace_stored(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.replace(b'key', b'value', noreply=False)
- assert result is True
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.replace(b'key', b'value', noreply=False)
- assert result is True
- def test_replace_not_stored(self):
- client = self.make_client([b'NOT_STORED\r\n'])
- result = client.replace(b'key', b'value', noreply=False)
- assert result is False
- # unit test for encoding passed in __init__
- client = self.make_client([b'NOT_STORED\r\n'], encoding='utf-8')
- result = client.replace(b'key', b'value', noreply=False)
- assert result is False
- def test_serialization(self):
- class JsonSerde(object):
- def serialize(self, key, value):
- return json.dumps(value).encode('ascii'), 0
- def deserialize(self, key, value, flags):
- return json.loads(value.decode('ascii'))
- client = self.make_client([b'STORED\r\n'], serde=JsonSerde())
- client.set('key', {'c': 'd'})
- assert client.sock.send_bufs == [
- b'set key 0 0 10 noreply\r\n{"c": "d"}\r\n'
- ]
- def test_serialization_flags(self):
- def _ser(key, value):
- return value, 1 if isinstance(value, int) else 0
- client = self.make_client(
- [b'STORED\r\n', b'STORED\r\n'], serializer=_ser)
- client.set_many(
- collections.OrderedDict([(b'a', b's'), (b'b', 0)]), noreply=False)
- assert client.sock.send_bufs == [
- b'set a 0 0 1\r\ns\r\nset b 1 0 1\r\n0\r\n'
- ]
- def test_serialization_overridden_flags(self):
- def _ser(key, value):
- return value, 1 if isinstance(value, int) else 0
- client = self.make_client(
- [b'STORED\r\n', b'STORED\r\n'], serializer=_ser)
- client.set_many(
- collections.OrderedDict([(b'a', b's'), (b'b', 0)]),
- noreply=False, flags=5)
- assert client.sock.send_bufs == [
- b'set a 5 0 1\r\ns\r\nset b 5 0 1\r\n0\r\n'
- ]
- def test_explicit_flags(self):
- client = self.make_client([b'STORED\r\n', b'STORED\r\n'])
- client.set_many(
- collections.OrderedDict([(b'a', b's'), (b'b', 0)]),
- noreply=False, flags=5)
- assert client.sock.send_bufs == [
- b'set a 5 0 1\r\ns\r\nset b 5 0 1\r\n0\r\n'
- ]
- def test_set_socket_handling(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.set(b'key', b'value', noreply=False)
- assert result is True
- assert client.sock.closed is False
- assert len(client.sock.send_bufs) == 1
- def test_set_error(self):
- client = self.make_client([b'ERROR\r\n'])
- def _set():
- client.set(b'key', b'value', noreply=False)
- with pytest.raises(MemcacheUnknownCommandError):
- _set()
- def test_set_exception(self):
- client = self.make_client([Exception('fail')])
- def _set():
- client.set(b'key', b'value', noreply=False)
- with pytest.raises(Exception):
- _set()
- assert client.sock is None
- def test_set_client_error(self):
- client = self.make_client([b'CLIENT_ERROR some message\r\n'])
- def _set():
- client.set('key', 'value', noreply=False)
- with pytest.raises(MemcacheClientError):
- _set()
- def test_set_server_error(self):
- client = self.make_client([b'SERVER_ERROR some message\r\n'])
- def _set():
- client.set(b'key', b'value', noreply=False)
- with pytest.raises(MemcacheServerError):
- _set()
- def test_closing_socket_on_unexpected_closed_error(self):
- client = self.make_client([
- b'VALUE ',
- MemcacheUnexpectedCloseError("foo bar"),
- ])
- def _set():
- client.set(b'key', b'value', noreply=False)
- with pytest.raises(MemcacheUnexpectedCloseError):
- _set()
- assert client.sock is None
- def test_set_unknown_error(self):
- client = self.make_client([b'foobarbaz\r\n'])
- def _set():
- client.set(b'key', b'value', noreply=False)
- with pytest.raises(MemcacheUnknownError):
- _set()
- def test_set_key_with_space(self):
- client = self.make_client([b''])
- def _set():
- client.set(b'key has space', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_key_with_newline(self):
- client = self.make_client([b''])
- def _set():
- client.set(b'key\n', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_key_with_carriage_return(self):
- client = self.make_client([b''])
- def _set():
- client.set(b'key\r', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_key_with_null_character(self):
- client = self.make_client([b''])
- def _set():
- client.set(b'key\00', b'value', noreply=False)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_key_with_noninteger_expire(self):
- client = self.make_client([b''])
- class _OneLike(object):
- """object that looks similar to the int 1"""
- def __str__(self):
- return "1"
- for noreply in (True, False):
- for expire in (1.5, _OneLike(), "1"):
- def _set():
- client.set(b'finekey', b'finevalue',
- noreply=noreply, expire=expire)
- with pytest.raises(MemcacheIllegalInputError):
- _set()
- def test_set_many_socket_handling(self):
- client = self.make_client([b'STORED\r\n'])
- result = client.set_many({b'key': b'value'}, noreply=False)
- assert result == []
- assert client.sock.closed is False
- assert len(client.sock.send_bufs) == 1
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n'], encoding='utf-8')
- result = client.set_many({b'key': b'value'}, noreply=False)
- assert result == []
- assert client.sock.closed is False
- assert len(client.sock.send_bufs) == 1
- def test_set_many_exception(self):
- client = self.make_client([b'STORED\r\n', Exception('fail')])
- def _set():
- client.set_many({b'key': b'value', b'other': b'value'},
- noreply=False)
- with pytest.raises(Exception):
- _set()
- assert client.sock is None
- # unit test for encoding passed in __init__()
- client = self.make_client([b'STORED\r\n', Exception('fail')],
- encoding='utf-8')
- def _set():
- client.set_many({b'key': b'value', b'other': b'value'},
- noreply=False)
- with pytest.raises(Exception):
- _set()
- assert client.sock is None
- def test_stats(self):
- client = self.make_client([b'STAT fake_stats 1\r\n', b'END\r\n'])
- result = client.stats()
- assert client.sock.send_bufs == [
- b'stats\r\n'
- ]
- assert result == {b'fake_stats': 1}
- def test_stats_with_args(self):
- client = self.make_client([b'STAT fake_stats 1\r\n', b'END\r\n'])
- result = client.stats('some_arg')
- assert client.sock.send_bufs == [
- b'stats some_arg\r\n'
- ]
- assert result == {b'fake_stats': 1}
- def test_stats_conversions(self):
- client = self.make_client([
- # Most stats are converted to int
- b'STAT cmd_get 2519\r\n',
- b'STAT cmd_set 3099\r\n',
- b'STAT evictions 939\r\n',
- # Unless they can't be, they remain str
- b'STAT libevent 2.0.19-stable\r\n',
- # Some named stats are explicitly converted
- b'STAT hash_is_expanding 0\r\n',
- b'STAT rusage_user 0.609165\r\n',
- b'STAT rusage_system 0.852791\r\n',
- b'STAT slab_reassign_running 1\r\n',
- b'STAT version 1.4.14\r\n',
- b'STAT umask 777\r\n',
- b'STAT auth_enabled_sasl yes\r\n',
- b'END\r\n',
- ])
- result = client.stats()
- assert client.sock.send_bufs == [
- b'stats\r\n'
- ]
- expected = {
- b'cmd_get': 2519,
- b'cmd_set': 3099,
- b'evictions': 939,
- b'libevent': b'2.0.19-stable',
- b'hash_is_expanding': False,
- b'rusage_user': 0.609165,
- b'rusage_system': 0.852791,
- b'slab_reassign_running': True,
- b'version': b'1.4.14',
- b'umask': 0o777,
- b'auth_enabled_sasl': True,
- }
- assert result == expected
- def test_stats_cachedump(self):
- client = self.make_client([b'ITEM bob [7 b; 0 s]\r\n', b'END\r\n'])
- result = client.stats('cachedump', '1', '1')
- assert client.sock.send_bufs == [
- b'stats cachedump 1 1\r\n'
- ]
- assert result == {b'bob': b'[7 b; 0 s]'}
- def test_cache_memlimit(self):
- client = self.make_client([b'OK\r\n'])
- result = client.cache_memlimit(8)
- assert client.sock.send_bufs == [
- b'cache_memlimit 8\r\n'
- ]
- assert result is True
- def test_python_dict_set_is_supported(self):
- client = self.make_client([b'STORED\r\n'])
- client[b'key'] = b'value'
- def test_python_dict_get_is_supported(self):
- client = self.make_client([b'VALUE key 0 5\r\nvalue\r\nEND\r\n'])
- assert client[b'key'] == b'value'
- def test_python_dict_get_not_found_is_supported(self):
- client = self.make_client([b'END\r\n'])
- def _get():
- client[b'key']
- with pytest.raises(KeyError):
- _get()
- def test_python_dict_del_is_supported(self):
- client = self.make_client([b'DELETED\r\n'])
- del client[b'key']
- def test_too_long_key(self):
- client = self.make_client([b'END\r\n'])
- with pytest.raises(MemcacheClientError):
- client.get(b'x' * 251)
- def test_too_long_unicode_key(self):
- client = self.make_client([b'STORED\r\n'], allow_unicode_keys=True)
- with pytest.raises(MemcacheClientError):
- client.get('my☃'*150)
- with pytest.raises(MemcacheClientError):
- client.get(u'\u0FFF'*150)
- def test_key_contains_space(self):
- client = self.make_client([b'END\r\n'])
- with pytest.raises(MemcacheClientError):
- client.get(b'abc xyz')
- def test_key_contains_nonascii(self):
- client = self.make_client([b'END\r\n'])
- with pytest.raises(MemcacheClientError):
- client.get(u'\u3053\u3093\u306b\u3061\u306f')
- def _default_noreply_false(self, cmd, args, response):
- client = self.make_client(response, default_noreply=False)
- result = getattr(client, cmd)(*args)
- assert result is False
- def _default_noreply_true(self, cmd, args, response):
- client = self.make_client(response, default_noreply=True)
- result = getattr(client, cmd)(*args)
- assert result is True
- def _default_noreply_true_and_empty_list(self, cmd, args, response):
- client = self.make_client(response, default_noreply=True)
- result = getattr(client, cmd)(*args)
- assert result == []
- def test_default_noreply_set(self):
- with pytest.raises(MemcacheUnknownError):
- self._default_noreply_false(
- 'set', (b'key', b'value'), [b'UNKNOWN\r\n'])
- self._default_noreply_false(
- 'set', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'set', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_set_many(self):
- with pytest.raises(MemcacheUnknownError):
- client = self.make_client([b'UNKNOWN\r\n'], default_noreply=False)
- result = client.set_many({b'key': b'value'})
- assert result == [b'key']
- self._default_noreply_true_and_empty_list(
- 'set_many', ({b'key': b'value'},), [b'NOT_STORED\r\n'])
- def test_default_noreply_add(self):
- self._default_noreply_false(
- 'add', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'add', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_replace(self):
- self._default_noreply_false(
- 'replace', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'replace', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_append(self):
- self._default_noreply_false(
- 'append', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'append', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_prepend(self):
- self._default_noreply_false(
- 'prepend', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'prepend', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_touch(self):
- self._default_noreply_false('touch', (b'key',), [b'NOT_FOUND\r\n'])
- self._default_noreply_true('touch', (b'key',), [b'NOT_FOUND\r\n'])
- def test_default_noreply_flush_all(self):
- self._default_noreply_false('flush_all', (),
- [b'__FAKE_RESPONSE__\r\n'])
- self._default_noreply_true('flush_all', (), [b'__FAKE_RESPONSE__\r\n'])
- def test_version_success(self):
- client = self.make_client([b'VERSION 1.2.3\r\n'],
- default_noreply=False)
- result = client.version()
- assert result == b'1.2.3'
- def test_version_exception(self):
- client = self.make_client([b'INVALID DATA\r\n'], default_noreply=False)
- with pytest.raises(MemcacheUnknownError):
- client.version()
- @pytest.mark.unit()
- class TestClientSocketConnect(unittest.TestCase):
- def test_socket_connect_ipv4(self):
- server = ('127.0.0.1', 11211)
- client = Client(server, socket_module=MockSocketModule())
- client._connect()
- assert client.sock.connections == [server]
- assert client.sock.family == socket.AF_INET
- timeout = 2
- connect_timeout = 3
- client = Client(
- server, connect_timeout=connect_timeout, timeout=timeout,
- socket_module=MockSocketModule())
- client._connect()
- assert client.sock.timeouts == [connect_timeout, timeout]
- client = Client(server, socket_module=MockSocketModule())
- client._connect()
- assert client.sock.socket_options == []
- client = Client(
- server, socket_module=MockSocketModule(), no_delay=True)
- client._connect()
- assert client.sock.socket_options == [(socket.IPPROTO_TCP,
- socket.TCP_NODELAY, 1)]
- def test_socket_connect_ipv6(self):
- server = ('::1', 11211)
- client = Client(server, socket_module=MockSocketModule())
- client._connect()
- assert client.sock.connections == [server + (0, 0)]
- assert client.sock.family == socket.AF_INET6
- timeout = 2
- connect_timeout = 3
- client = Client(
- server, connect_timeout=connect_timeout, timeout=timeout,
- socket_module=MockSocketModule())
- client._connect()
- assert client.sock.timeouts == [connect_timeout, timeout]
- client = Client(server, socket_module=MockSocketModule())
- client._connect()
- assert client.sock.socket_options == []
- client = Client(
- server, socket_module=MockSocketModule(), no_delay=True)
- client._connect()
- assert client.sock.socket_options == [(socket.IPPROTO_TCP,
- socket.TCP_NODELAY, 1)]
- def test_socket_connect_unix(self):
- server = '/tmp/pymemcache.{pid}'.format(pid=os.getpid())
- with MockUnixSocketServer(server):
- client = Client(server)
- client._connect()
- assert client.sock.family == socket.AF_UNIX
- @unittest.skipIf('Linux' != platform.system(),
- 'Socket keepalive only support Linux platforms.')
- def test_linux_socket_keepalive(self):
- server = ('::1', 11211)
- try:
- client = Client(
- server,
- socket_module=MockSocketModule(),
- socket_keepalive=KeepaliveOpts())
- client._connect()
- except SystemError:
- self.fail("SystemError unexpectedly raised")
- with self.assertRaises(ValueError):
- # A KeepaliveOpts object is expected, a ValueError will be raised
- Client(
- server,
- socket_module=MockSocketModule(),
- socket_keepalive=True)
- @mock.patch('platform.system')
- def test_osx_socket_keepalive(self, platform_mock):
- platform_mock.return_value = 'Darwin'
- server = ('::1', 11211)
- # For the moment the socket keepalive is only implemented for Linux
- with self.assertRaises(SystemError):
- Client(
- server,
- socket_module=MockSocketModule(),
- socket_keepalive=KeepaliveOpts())
- @mock.patch('platform.system')
- def test_windows_socket_keepalive(self, platform_mock):
- platform_mock.return_value = 'Windows'
- server = ('::1', 11211)
- # For the moment the socket keepalive is only implemented for Linux
- with self.assertRaises(SystemError):
- Client(
- server,
- socket_module=MockSocketModule(),
- socket_keepalive=KeepaliveOpts())
- def test_socket_connect_closes_on_failure(self):
- server = ("example.com", 11211)
- socket_module = MockSocketModule(connect_failure=OSError())
- client = Client(server, socket_module=socket_module)
- with pytest.raises(OSError):
- client._connect()
- assert len(socket_module.sockets) == 1
- assert socket_module.sockets[0].connections == []
- assert socket_module.sockets[0].closed
- def test_socket_close(self):
- server = ("example.com", 11211)
- client = Client(server, socket_module=MockSocketModule())
- client._connect()
- assert client.sock is not None
- client.close()
- assert client.sock is None
- def test_socket_close_exception(self):
- server = ("example.com", 11211)
- socket_module = MockSocketModule(close_failure=OSError())
- client = Client(server, socket_module=socket_module)
- client._connect()
- assert client.sock is not None
- client.close()
- assert client.sock is None
- class TestPooledClient(ClientTestMixin, unittest.TestCase):
- def make_client(self, mock_socket_values, **kwargs):
- mock_client = Client(None, **kwargs)
- mock_client.sock = MockSocket(list(mock_socket_values))
- client = PooledClient(None, **kwargs)
- client.client_pool = pool.ObjectPool(lambda: mock_client)
- return client
- def _default_noreply_false(self, cmd, args, response):
- client = self.make_client(response, default_noreply=False)
- result = getattr(client, cmd)(*args)
- assert result is False
- def _default_noreply_true(self, cmd, args, response):
- client = self.make_client(response, default_noreply=True)
- result = getattr(client, cmd)(*args)
- assert result is True
- def _default_noreply_true_and_empty_list(self, cmd, args, response):
- client = self.make_client(response, default_noreply=True)
- result = getattr(client, cmd)(*args)
- assert result == []
- def test_default_noreply_set(self):
- with pytest.raises(MemcacheUnknownError):
- self._default_noreply_false(
- 'set', (b'key', b'value'), [b'UNKNOWN\r\n'])
- self._default_noreply_false(
- 'set', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'set', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_set_many(self):
- with pytest.raises(MemcacheUnknownError):
- client = self.make_client([b'UNKNOWN\r\n'], default_noreply=False)
- client.set_many({b'key': b'value'})
- self._default_noreply_true_and_empty_list(
- 'set_many', ({b'key': b'value'},), [b'NOT_STORED\r\n'])
- def test_default_noreply_add(self):
- self._default_noreply_false(
- 'add', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'add', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_replace(self):
- self._default_noreply_false(
- 'replace', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'replace', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_append(self):
- self._default_noreply_false(
- 'append', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'append', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_prepend(self):
- self._default_noreply_false(
- 'prepend', (b'key', b'value'), [b'NOT_STORED\r\n'])
- self._default_noreply_true(
- 'prepend', (b'key', b'value'), [b'NOT_STORED\r\n'])
- def test_default_noreply_touch(self):
- self._default_noreply_false('touch', (b'key',), [b'NOT_FOUND\r\n'])
- self._default_noreply_true('touch', (b'key',), [b'NOT_FOUND\r\n'])
- def test_default_noreply_flush_all(self):
- self._default_noreply_false('flush_all', (),
- [b'__FAKE_RESPONSE__\r\n'])
- self._default_noreply_true('flush_all', (), [b'__FAKE_RESPONSE__\r\n'])
- def test_custom_client(self):
- class MyClient(Client):
- pass
- client = PooledClient(('host', 11211))
- client.client_class = MyClient
- assert isinstance(client.client_pool.get(), MyClient)
- class TestPooledClientIdleTimeout(ClientTestMixin, unittest.TestCase):
- def make_client(self, mock_socket_values, **kwargs):
- mock_client = Client(None, **kwargs)
- mock_client.sock = MockSocket(list(mock_socket_values))
- client = PooledClient(None, pool_idle_timeout=60, **kwargs)
- client.client_pool = pool.ObjectPool(lambda: mock_client)
- return client
- def test_free_idle(self):
- class Counter(object):
- count = 0
- def increment(self, obj):
- self.count += 1
- removed = Counter()
- client = self.make_client([b'VALUE key 0 5\r\nvalue\r\nEND\r\n']*2)
- client.client_pool._after_remove = removed.increment
- client.client_pool._idle_clock = lambda: 0
- client.set(b'key', b'value')
- assert removed.count == 0
- client.get(b'key')
- assert removed.count == 0
- # Advance clock to beyond the idle timeout.
- client.client_pool._idle_clock = lambda: 61
- client.get(b'key')
- assert removed.count == 1
- class TestMockClient(ClientTestMixin, unittest.TestCase):
- def make_client(self, mock_socket_values, **kwargs):
- client = MockMemcacheClient(None, **kwargs)
- client.sock = MockSocket(list(mock_socket_values))
- return client
- def test_get_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'VALUE key 0 5\r\nvalue\r\nEND\r\n',
- ])
- result = client.set(b'key', b'value', noreply=False)
- result = client.get(b'key')
- assert result == b'value'
- def test_deserialization(self):
- class JsonSerde(object):
- def serialize(self, key, value):
- if isinstance(value, dict):
- return json.dumps(value).encode('UTF-8'), 1
- return value, 0
- def deserialize(self, key, value, flags):
- if flags == 1:
- return json.loads(value.decode('UTF-8'))
- return value
- client = self.make_client([
- b'STORED\r\n',
- b'VALUE key1 0 5\r\nhello\r\nEND\r\n',
- b'STORED\r\n',
- b'VALUE key2 0 18\r\n{"hello": "world"}\r\nEND\r\n',
- ], serde=JsonSerde())
- result = client.set(b'key1', b'hello', noreply=False)
- result = client.get(b'key1')
- assert result == b'hello'
- result = client.set(b'key2', dict(hello='world'), noreply=False)
- result = client.get(b'key2')
- assert result == dict(hello='world')
- class TestPrefixedClient(ClientTestMixin, unittest.TestCase):
- def make_client(self, mock_socket_values, **kwargs):
- client = Client(None, key_prefix=b'xyz:', **kwargs)
- client.sock = MockSocket(list(mock_socket_values))
- return client
- def test_get_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'VALUE xyz:key 0 5\r\nvalue\r\nEND\r\n',
- ])
- result = client.set(b'key', b'value', noreply=False)
- result = client.get(b'key')
- assert result == b'value'
- def test_get_many_some_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'VALUE xyz:key1 0 6\r\nvalue1\r\nEND\r\n',
- ])
- result = client.set(b'key1', b'value1', noreply=False)
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1'}
- def test_get_many_all_found(self):
- client = self.make_client([
- b'STORED\r\n',
- b'STORED\r\n',
- b'VALUE xyz:key1 0 6\r\nvalue1\r\n',
- b'VALUE xyz:key2 0 6\r\nvalue2\r\nEND\r\n',
- ])
- result = client.set(b'key1', b'value1', noreply=False)
- result = client.set(b'key2', b'value2', noreply=False)
- result = client.get_many([b'key1', b'key2'])
- assert result == {b'key1': b'value1', b'key2': b'value2'}
- def test_python_dict_get_is_supported(self):
- client = self.make_client([b'VALUE xyz:key 0 5\r\nvalue\r\nEND\r\n'])
- assert client[b'key'] == b'value'
- class TestPrefixedPooledClient(TestPrefixedClient):
- def make_client(self, mock_socket_values, **kwargs):
- mock_client = Client(None, key_prefix=b'xyz:', **kwargs)
- mock_client.sock = MockSocket(list(mock_socket_values))
- client = PooledClient(None, key_prefix=b'xyz:', **kwargs)
- client.client_pool = pool.ObjectPool(lambda: mock_client)
- return client
- @pytest.mark.unit()
- class TestRetryOnEINTR(unittest.TestCase):
- def make_client(self, values):
- client = Client(None)
- client.sock = MockSocket(list(values))
- return client
- def test_recv(self):
- client = self.make_client([
- b'VALUE ',
- socket.error(errno.EINTR, "Interrupted system call"),
- b'key1 0 6\r\nval',
- socket.error(errno.EINTR, "Interrupted system call"),
- b'ue1\r\nEND\r\n',
- ])
- assert client[b'key1'] == b'value1'
- @pytest.mark.unit()
- class TestNormalizeServerSpec(unittest.TestCase):
- def test_normalize_server_spec(self):
- f = normalize_server_spec
- assert f(None) is None
- assert f(('127.0.0.1', 12345)) == ('127.0.0.1', 12345)
- assert f(['127.0.0.1', 12345]) == ('127.0.0.1', 12345)
- assert f('unix:/run/memcached/socket') == '/run/memcached/socket'
- assert f('/run/memcached/socket') == '/run/memcached/socket'
- assert f('localhost') == ('localhost', 11211)
- assert f('localhost:12345') == ('localhost', 12345)
- assert f('[::1]') == ('::1', 11211)
- assert f('[::1]:12345') == ('::1', 12345)
- assert f('127.0.0.1') == ('127.0.0.1', 11211)
- assert f('127.0.0.1:12345') == ('127.0.0.1', 12345)
- with pytest.raises(ValueError) as excinfo:
- f({'host': 12345})
- assert str(excinfo.value) == "Unknown server provided: {'host': 12345}"
- with pytest.raises(ValueError) as excinfo:
- f(12345)
- assert str(excinfo.value) == "Unknown server provided: 12345"
- @pytest.mark.unit()
- class TestKeepaliveopts(unittest.TestCase):
- def test_keepalive_opts(self):
- kao = KeepaliveOpts()
- assert (kao.idle == 1 and kao.intvl == 1 and kao.cnt == 5)
- kao = KeepaliveOpts(idle=1, intvl=4, cnt=2)
- assert (kao.idle == 1 and kao.intvl == 4 and kao.cnt == 2)
- kao = KeepaliveOpts(idle=8)
- assert (kao.idle == 8 and kao.intvl == 1 and kao.cnt == 5)
- kao = KeepaliveOpts(cnt=8)
- assert (kao.idle == 1 and kao.intvl == 1 and kao.cnt == 8)
- with self.assertRaises(ValueError):
- KeepaliveOpts(cnt=0)
- with self.assertRaises(ValueError):
- KeepaliveOpts(idle=-1)
|