~ubuntu-branches/ubuntu/wily/pymongo/wily-proposed

« back to all changes in this revision

Viewing changes to test/pymongo_mocks.py

  • Committer: Package Import Robot
  • Author(s): Federico Ceratto
  • Date: 2015-04-26 22:43:13 UTC
  • mfrom: (24.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20150426224313-0hga2jphvf0rrmfe
Tags: 3.0.1-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2013-2014 MongoDB, Inc.
 
1
# Copyright 2013-2015 MongoDB, Inc.
2
2
#
3
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
4
# you may not use this file except in compliance with the License.
14
14
 
15
15
"""Tools for mocking parts of PyMongo to test other parts."""
16
16
 
17
 
import socket
 
17
import contextlib
 
18
from functools import partial
 
19
import weakref
18
20
 
19
21
from pymongo import common
20
 
from pymongo import MongoClient, MongoReplicaSetClient
21
 
from pymongo.pool import Pool
 
22
from pymongo import MongoClient
 
23
from pymongo.errors import AutoReconnect, NetworkTimeout
 
24
from pymongo.ismaster import IsMaster
 
25
from pymongo.monitor import Monitor
 
26
from pymongo.pool import Pool, PoolOptions
 
27
from pymongo.server_description import ServerDescription
22
28
 
23
29
from test import host as default_host, port as default_port
24
 
from test.utils import my_partial
25
30
 
26
31
 
27
32
class MockPool(Pool):
28
33
    def __init__(self, client, pair, *args, **kwargs):
29
 
        # MockPool gets a 'client' arg, regular pools don't.
30
 
        self.client = client
 
34
        # MockPool gets a 'client' arg, regular pools don't. Weakref it to
 
35
        # avoid cycle with __del__, causing ResourceWarnings in Python 3.3.
 
36
        self.client = weakref.proxy(client)
31
37
        self.mock_host, self.mock_port = pair
32
38
 
33
39
        # Actually connect to the default server.
34
 
        Pool.__init__(
35
 
            self,
36
 
            pair=(default_host, default_port),
37
 
            max_size=None,
38
 
            net_timeout=None,
39
 
            conn_timeout=20,
40
 
            use_ssl=False,
41
 
            use_greenlets=False)
 
40
        Pool.__init__(self,
 
41
                      (default_host, default_port),
 
42
                      PoolOptions(connect_timeout=20))
42
43
 
43
 
    def get_socket(self, force=False):
 
44
    @contextlib.contextmanager
 
45
    def get_socket(self, all_credentials, checkout=False):
44
46
        client = self.client
45
47
        host_and_port = '%s:%s' % (self.mock_host, self.mock_port)
46
48
        if host_and_port in client.mock_down_hosts:
47
 
            raise socket.error('mock error')
 
49
            raise AutoReconnect('mock error')
48
50
 
49
51
        assert host_and_port in (
50
52
            client.mock_standalones
51
53
            + client.mock_members
52
54
            + client.mock_mongoses), "bad host: %s" % host_and_port
53
55
 
54
 
        sock_info = Pool.get_socket(self, force)
55
 
        sock_info.mock_host = self.mock_host
56
 
        sock_info.mock_port = self.mock_port
57
 
        return sock_info
58
 
 
59
 
 
60
 
class MockClientBase(object):
61
 
    def __init__(self, standalones, members, mongoses, config):
62
 
        """standalones, etc., are like ['a:1', 'b:2']"""
 
56
        with Pool.get_socket(self, all_credentials) as sock_info:
 
57
            sock_info.mock_host = self.mock_host
 
58
            sock_info.mock_port = self.mock_port
 
59
            yield sock_info
 
60
 
 
61
 
 
62
class MockMonitor(Monitor):
 
63
    def __init__(
 
64
            self,
 
65
            client,
 
66
            server_description,
 
67
            topology,
 
68
            pool,
 
69
            topology_settings):
 
70
        # MockMonitor gets a 'client' arg, regular monitors don't.
 
71
        self.client = client
 
72
        Monitor.__init__(
 
73
            self,
 
74
            server_description,
 
75
            topology,
 
76
            pool,
 
77
            topology_settings)
 
78
 
 
79
    def _check_once(self):
 
80
        address = self._server_description.address
 
81
        response, rtt = self.client.mock_is_master('%s:%d' % address)
 
82
        return ServerDescription(address, IsMaster(response), rtt)
 
83
 
 
84
 
 
85
class MockClient(MongoClient):
 
86
    def __init__(
 
87
            self, standalones, members, mongoses, ismaster_hosts=None,
 
88
            *args, **kwargs):
 
89
        """A MongoClient connected to the default server, with a mock topology.
 
90
 
 
91
        standalones, members, mongoses determine the configuration of the
 
92
        topology. They are formatted like ['a:1', 'b:2']. ismaster_hosts
 
93
        provides an alternative host list for the server's mocked ismaster
 
94
        response; see test_connect_with_internal_ips.
 
95
        """
63
96
        self.mock_standalones = standalones[:]
64
97
        self.mock_members = members[:]
65
98
 
68
101
        else:
69
102
            self.mock_primary = None
70
103
 
71
 
        if config is not None:
72
 
            self.mock_ismaster_hosts = config
 
104
        if ismaster_hosts is not None:
 
105
            self.mock_ismaster_hosts = ismaster_hosts
73
106
        else:
74
107
            self.mock_ismaster_hosts = members[:]
75
108
 
84
117
        # Hostname -> max write batch size
85
118
        self.mock_max_write_batch_sizes = {}
86
119
 
 
120
        # Hostname -> round trip time
 
121
        self.mock_rtts = {}
 
122
 
 
123
        kwargs['_pool_class'] = partial(MockPool, self)
 
124
        kwargs['_monitor_class'] = partial(MockMonitor, self)
 
125
 
 
126
        super(MockClient, self).__init__(*args, **kwargs)
 
127
 
87
128
    def kill_host(self, host):
88
129
        """Host is like 'a:1'."""
89
130
        self.mock_down_hosts.append(host)
99
140
        self.mock_max_write_batch_sizes[host] = size
100
141
 
101
142
    def mock_is_master(self, host):
 
143
        """Return mock ismaster response (a dict) and round trip time."""
102
144
        min_wire_version, max_wire_version = self.mock_wire_versions.get(
103
145
            host,
104
146
            (common.MIN_WIRE_VERSION, common.MAX_WIRE_VERSION))
106
148
        max_write_batch_size = self.mock_max_write_batch_sizes.get(
107
149
            host, common.MAX_WRITE_BATCH_SIZE)
108
150
 
 
151
        rtt = self.mock_rtts.get(host, 0)
 
152
 
109
153
        # host is like 'a:1'.
110
154
        if host in self.mock_down_hosts:
111
 
            raise socket.timeout('mock timeout')
 
155
            raise NetworkTimeout('mock timeout')
112
156
 
113
 
        if host in self.mock_standalones:
114
 
            return {
 
157
        elif host in self.mock_standalones:
 
158
            response = {
 
159
                'ok': 1,
115
160
                'ismaster': True,
116
161
                'minWireVersion': min_wire_version,
117
162
                'maxWireVersion': max_wire_version,
118
163
                'maxWriteBatchSize': max_write_batch_size}
119
 
 
120
 
        if host in self.mock_members:
 
164
        elif host in self.mock_members:
121
165
            ismaster = (host == self.mock_primary)
122
166
 
123
167
            # Simulate a replica set member.
124
168
            response = {
 
169
                'ok': 1,
125
170
                'ismaster': ismaster,
126
171
                'secondary': not ismaster,
127
172
                'setName': 'rs',
132
177
 
133
178
            if self.mock_primary:
134
179
                response['primary'] = self.mock_primary
135
 
 
136
 
            return response
137
 
 
138
 
        if host in self.mock_mongoses:
139
 
            return {
 
180
        elif host in self.mock_mongoses:
 
181
            response = {
 
182
                'ok': 1,
140
183
                'ismaster': True,
141
184
                'minWireVersion': min_wire_version,
142
185
                'maxWireVersion': max_wire_version,
143
186
                'msg': 'isdbgrid',
144
187
                'maxWriteBatchSize': max_write_batch_size}
145
 
 
146
 
        # In test_internal_ips(), we try to connect to a host listed
147
 
        # in ismaster['hosts'] but not publicly accessible.
148
 
        raise socket.error('Unknown host: %s' % host)
149
 
 
150
 
    def simple_command(self, sock_info, dbname, spec):
151
 
        # __simple_command is also used for authentication, but in this
152
 
        # test it's only used for ismaster.
153
 
        assert spec == {'ismaster': 1}
154
 
        response = self.mock_is_master(
155
 
            '%s:%s' % (sock_info.mock_host, sock_info.mock_port))
156
 
 
157
 
        ping_time = 10
158
 
        return response, ping_time
159
 
 
160
 
 
161
 
class MockClient(MockClientBase, MongoClient):
162
 
    def __init__(
163
 
        self, standalones, members, mongoses, ismaster_hosts=None,
164
 
        *args, **kwargs
165
 
    ):
166
 
        MockClientBase.__init__(
167
 
            self, standalones, members, mongoses, ismaster_hosts)
168
 
 
169
 
        kwargs['_pool_class'] = my_partial(MockPool, self)
170
 
        MongoClient.__init__(self, *args, **kwargs)
171
 
 
172
 
    def _MongoClient__simple_command(self, sock_info, dbname, spec):
173
 
        return self.simple_command(sock_info, dbname, spec)
174
 
 
175
 
 
176
 
class MockReplicaSetClient(MockClientBase, MongoReplicaSetClient):
177
 
    def __init__(
178
 
        self, standalones, members, mongoses, ismaster_hosts=None,
179
 
        *args, **kwargs
180
 
    ):
181
 
        MockClientBase.__init__(
182
 
            self, standalones, members, mongoses, ismaster_hosts)
183
 
 
184
 
        kwargs['_pool_class'] = my_partial(MockPool, self)
185
 
        MongoReplicaSetClient.__init__(self, *args, **kwargs)
186
 
 
187
 
    def _MongoReplicaSetClient__is_master(self, host):
188
 
        response = self.mock_is_master('%s:%s' % host)
189
 
        connection_pool = MockPool(self, host)
190
 
        ping_time = 10
191
 
        return response, connection_pool, ping_time
192
 
 
193
 
    def _MongoReplicaSetClient__simple_command(self, sock_info, dbname, spec):
194
 
        return self.simple_command(sock_info, dbname, spec)
 
188
        else:
 
189
            # In test_internal_ips(), we try to connect to a host listed
 
190
            # in ismaster['hosts'] but not publicly accessible.
 
191
            raise AutoReconnect('Unknown host: %s' % host)
 
192
 
 
193
        return response, rtt
 
194
 
 
195
    def _process_kill_cursors_queue(self):
 
196
        # Avoid the background thread causing races, e.g. a surprising
 
197
        # reconnect while we're trying to test a disconnected client.
 
198
        pass