mercurial/httpclient/tests/util.py
changeset 14243 861f28212398
child 14341 5c3de67e7402
equal deleted inserted replaced
14242:5ee1309f7edb 14243:861f28212398
       
     1 # Copyright 2010, Google Inc.
       
     2 # All rights reserved.
       
     3 #
       
     4 # Redistribution and use in source and binary forms, with or without
       
     5 # modification, are permitted provided that the following conditions are
       
     6 # met:
       
     7 #
       
     8 #     * Redistributions of source code must retain the above copyright
       
     9 # notice, this list of conditions and the following disclaimer.
       
    10 #     * Redistributions in binary form must reproduce the above
       
    11 # copyright notice, this list of conditions and the following disclaimer
       
    12 # in the documentation and/or other materials provided with the
       
    13 # distribution.
       
    14 #     * Neither the name of Google Inc. nor the names of its
       
    15 # contributors may be used to endorse or promote products derived from
       
    16 # this software without specific prior written permission.
       
    17 
       
    18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
    22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    29 import difflib
       
    30 import socket
       
    31 
       
    32 import http
       
    33 
       
    34 
       
    35 class MockSocket(object):
       
    36     """Mock non-blocking socket object.
       
    37 
       
    38     This is ONLY capable of mocking a nonblocking socket.
       
    39 
       
    40     Attributes:
       
    41       early_data: data to always send as soon as end of headers is seen
       
    42       data: a list of strings to return on recv(), with the
       
    43             assumption that the socket would block between each
       
    44             string in the list.
       
    45       read_wait_sentinel: data that must be written to the socket before
       
    46                           beginning the response.
       
    47       close_on_empty: If true, close the socket when it runs out of data
       
    48                       for the client.
       
    49     """
       
    50     def __init__(self, af, socktype, proto):
       
    51         self.af = af
       
    52         self.socktype = socktype
       
    53         self.proto = proto
       
    54 
       
    55         self.early_data = []
       
    56         self.data = []
       
    57         self.remote_closed = self.closed = False
       
    58         self.close_on_empty = False
       
    59         self.sent = ''
       
    60         self.read_wait_sentinel = http._END_HEADERS
       
    61 
       
    62     def close(self):
       
    63         self.closed = True
       
    64 
       
    65     def connect(self, sa):
       
    66         self.sa = sa
       
    67 
       
    68     def setblocking(self, timeout):
       
    69         assert timeout == 0
       
    70 
       
    71     def recv(self, amt=-1):
       
    72         if self.early_data:
       
    73             datalist = self.early_data
       
    74         elif not self.data:
       
    75             return ''
       
    76         else:
       
    77             datalist = self.data
       
    78         if amt == -1:
       
    79             return datalist.pop(0)
       
    80         data = datalist.pop(0)
       
    81         if len(data) > amt:
       
    82             datalist.insert(0, data[amt:])
       
    83         if not self.data and not self.early_data and self.close_on_empty:
       
    84             self.remote_closed = True
       
    85         return data[:amt]
       
    86 
       
    87     @property
       
    88     def ready_for_read(self):
       
    89         return ((self.early_data and http._END_HEADERS in self.sent)
       
    90                 or (self.read_wait_sentinel in self.sent and self.data)
       
    91                 or self.closed)
       
    92 
       
    93     def send(self, data):
       
    94         # this is a horrible mock, but nothing needs us to raise the
       
    95         # correct exception yet
       
    96         assert not self.closed, 'attempted to write to a closed socket'
       
    97         assert not self.remote_closed, ('attempted to write to a'
       
    98                                         ' socket closed by the server')
       
    99         if len(data) > 8192:
       
   100             data = data[:8192]
       
   101         self.sent += data
       
   102         return len(data)
       
   103 
       
   104 
       
   105 def mockselect(r, w, x, timeout=0):
       
   106     """Simple mock for select()
       
   107     """
       
   108     readable = filter(lambda s: s.ready_for_read, r)
       
   109     return readable, w[:], []
       
   110 
       
   111 
       
   112 def mocksslwrap(sock, keyfile=None, certfile=None,
       
   113                 server_side=False, cert_reqs=http.socketutil.CERT_NONE,
       
   114                 ssl_version=http.socketutil.PROTOCOL_SSLv23, ca_certs=None,
       
   115                 do_handshake_on_connect=True,
       
   116                 suppress_ragged_eofs=True):
       
   117     return sock
       
   118 
       
   119 
       
   120 def mockgetaddrinfo(host, port, unused, streamtype):
       
   121     assert unused == 0
       
   122     assert streamtype == socket.SOCK_STREAM
       
   123     if host.count('.') != 3:
       
   124         host = '127.0.0.42'
       
   125     return [(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, '',
       
   126              (host, port))]
       
   127 
       
   128 
       
   129 class HttpTestBase(object):
       
   130     def setUp(self):
       
   131         self.orig_socket = socket.socket
       
   132         socket.socket = MockSocket
       
   133 
       
   134         self.orig_getaddrinfo = socket.getaddrinfo
       
   135         socket.getaddrinfo = mockgetaddrinfo
       
   136 
       
   137         self.orig_select = http.select.select
       
   138         http.select.select = mockselect
       
   139 
       
   140         self.orig_sslwrap = http.socketutil.wrap_socket
       
   141         http.socketutil.wrap_socket = mocksslwrap
       
   142 
       
   143     def tearDown(self):
       
   144         socket.socket = self.orig_socket
       
   145         http.select.select = self.orig_select
       
   146         http.socketutil.wrap_socket = self.orig_sslwrap
       
   147         socket.getaddrinfo = self.orig_getaddrinfo
       
   148 
       
   149     def assertStringEqual(self, l, r):
       
   150         try:
       
   151             self.assertEqual(l, r, ('failed string equality check, '
       
   152                                     'see stdout for details'))
       
   153         except:
       
   154             add_nl = lambda li: map(lambda x: x + '\n', li)
       
   155             print 'failed expectation:'
       
   156             print ''.join(difflib.unified_diff(
       
   157                 add_nl(l.splitlines()), add_nl(r.splitlines()),
       
   158                 fromfile='expected', tofile='got'))
       
   159             raise
       
   160 # no-check-code