~ntt-pf-lab/nova/monkey_patch_notification

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/test/test_tcp_internals.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2006 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
"""
 
5
Whitebox tests for TCP APIs.
 
6
"""
 
7
 
 
8
import errno, socket, os
 
9
 
 
10
try:
 
11
    import resource
 
12
except ImportError:
 
13
    resource = None
 
14
 
 
15
from twisted.trial.unittest import TestCase
 
16
 
 
17
from twisted.python import log
 
18
from twisted.internet.tcp import ECONNABORTED, ENOMEM, ENFILE, EMFILE, ENOBUFS, EINPROGRESS, Port
 
19
from twisted.internet.protocol import ServerFactory
 
20
from twisted.python.runtime import platform
 
21
from twisted.internet.defer import maybeDeferred, gatherResults
 
22
from twisted.internet import reactor, interfaces
 
23
 
 
24
 
 
25
class PlatformAssumptionsTestCase(TestCase):
 
26
    """
 
27
    Test assumptions about platform behaviors.
 
28
    """
 
29
    socketLimit = 8192
 
30
 
 
31
    def setUp(self):
 
32
        self.openSockets = []
 
33
        if resource is not None:
 
34
            self.originalFileLimit = resource.getrlimit(resource.RLIMIT_NOFILE)
 
35
            resource.setrlimit(resource.RLIMIT_NOFILE, (128, self.originalFileLimit[1]))
 
36
            self.socketLimit = 256
 
37
 
 
38
 
 
39
    def tearDown(self):
 
40
        while self.openSockets:
 
41
            self.openSockets.pop().close()
 
42
        if resource is not None:
 
43
            # OS X implicitly lowers the hard limit in the setrlimit call
 
44
            # above.  Retrieve the new hard limit to pass in to this
 
45
            # setrlimit call, so that it doesn't give us a permission denied
 
46
            # error.
 
47
            currentHardLimit = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
 
48
            newSoftLimit = min(self.originalFileLimit[0], currentHardLimit)
 
49
            resource.setrlimit(resource.RLIMIT_NOFILE, (newSoftLimit, currentHardLimit))
 
50
 
 
51
 
 
52
    def socket(self):
 
53
        """
 
54
        Create and return a new socket object, also tracking it so it can be
 
55
        closed in the test tear down.
 
56
        """
 
57
        s = socket.socket()
 
58
        self.openSockets.append(s)
 
59
        return s
 
60
 
 
61
 
 
62
    def test_acceptOutOfFiles(self):
 
63
        """
 
64
        Test that the platform accept(2) call fails with either L{EMFILE} or
 
65
        L{ENOBUFS} when there are too many file descriptors open.
 
66
        """
 
67
        # Make a server to which to connect
 
68
        port = self.socket()
 
69
        port.bind(('127.0.0.1', 0))
 
70
        serverPortNumber = port.getsockname()[1]
 
71
        port.listen(5)
 
72
 
 
73
        # Use up all the file descriptors
 
74
        for i in xrange(self.socketLimit):
 
75
            try:
 
76
                self.socket()
 
77
            except socket.error, e:
 
78
                if e.args[0] in (EMFILE, ENOBUFS):
 
79
                    self.openSockets.pop().close()
 
80
                    break
 
81
                else:
 
82
                    raise
 
83
        else:
 
84
            self.fail("Could provoke neither EMFILE nor ENOBUFS from platform.")
 
85
 
 
86
        # Make a client to use to connect to the server
 
87
        client = self.socket()
 
88
        client.setblocking(False)
 
89
 
 
90
        # Non-blocking connect is supposed to fail, but this is not true
 
91
        # everywhere (e.g. freeBSD)
 
92
        self.assertIn(client.connect_ex(('127.0.0.1', serverPortNumber)),
 
93
                      (0, EINPROGRESS))
 
94
 
 
95
        # Make sure that the accept call fails in the way we expect.
 
96
        exc = self.assertRaises(socket.error, port.accept)
 
97
        self.assertIn(exc.args[0], (EMFILE, ENOBUFS))
 
98
    if platform.getType() == "win32":
 
99
        test_acceptOutOfFiles.skip = (
 
100
            "Windows requires an unacceptably large amount of resources to "
 
101
            "provoke this behavior in the naive manner.")
 
102
 
 
103
 
 
104
 
 
105
class SelectReactorTestCase(TestCase):
 
106
    """
 
107
    Tests for select-specific failure conditions.
 
108
    """
 
109
 
 
110
    def setUp(self):
 
111
        self.ports = []
 
112
        self.messages = []
 
113
        log.addObserver(self.messages.append)
 
114
 
 
115
 
 
116
    def tearDown(self):
 
117
        log.removeObserver(self.messages.append)
 
118
        return gatherResults([
 
119
            maybeDeferred(p.stopListening)
 
120
            for p in self.ports])
 
121
 
 
122
 
 
123
    def port(self, portNumber, factory, interface):
 
124
        """
 
125
        Create, start, and return a new L{Port}, also tracking it so it can
 
126
        be stopped in the test tear down.
 
127
        """
 
128
        p = Port(portNumber, factory, interface=interface)
 
129
        p.startListening()
 
130
        self.ports.append(p)
 
131
        return p
 
132
 
 
133
 
 
134
    def _acceptFailureTest(self, socketErrorNumber):
 
135
        """
 
136
        Test behavior in the face of an exception from C{accept(2)}.
 
137
 
 
138
        On any exception which indicates the platform is unable or unwilling
 
139
        to allocate further resources to us, the existing port should remain
 
140
        listening, a message should be logged, and the exception should not
 
141
        propagate outward from doRead.
 
142
 
 
143
        @param socketErrorNumber: The errno to simulate from accept.
 
144
        """
 
145
        class FakeSocket(object):
 
146
            """
 
147
            Pretend to be a socket in an overloaded system.
 
148
            """
 
149
            def accept(self):
 
150
                raise socket.error(
 
151
                    socketErrorNumber, os.strerror(socketErrorNumber))
 
152
 
 
153
        factory = ServerFactory()
 
154
        port = self.port(0, factory, interface='127.0.0.1')
 
155
        originalSocket = port.socket
 
156
        try:
 
157
            port.socket = FakeSocket()
 
158
 
 
159
            port.doRead()
 
160
 
 
161
            expectedFormat = "Could not accept new connection (%s)"
 
162
            expectedErrorCode = errno.errorcode[socketErrorNumber]
 
163
            expectedMessage = expectedFormat % (expectedErrorCode,)
 
164
            for msg in self.messages:
 
165
                if msg.get('message') == (expectedMessage,):
 
166
                    break
 
167
            else:
 
168
                self.fail("Log event for failed accept not found in "
 
169
                          "%r" % (self.messages,))
 
170
        finally:
 
171
            port.socket = originalSocket
 
172
 
 
173
 
 
174
    def test_tooManyFilesFromAccept(self):
 
175
        """
 
176
        C{accept(2)} can fail with C{EMFILE} when there are too many open file
 
177
        descriptors in the process.  Test that this doesn't negatively impact
 
178
        any other existing connections.
 
179
 
 
180
        C{EMFILE} mainly occurs on Linux when the open file rlimit is
 
181
        encountered.
 
182
        """
 
183
        return self._acceptFailureTest(EMFILE)
 
184
 
 
185
 
 
186
    def test_noBufferSpaceFromAccept(self):
 
187
        """
 
188
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
 
189
        C{accept(2)} fails with C{ENOBUFS}.
 
190
 
 
191
        This mainly occurs on Windows and FreeBSD, but may be possible on
 
192
        Linux and other platforms as well.
 
193
        """
 
194
        return self._acceptFailureTest(ENOBUFS)
 
195
 
 
196
 
 
197
    def test_connectionAbortedFromAccept(self):
 
198
        """
 
199
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
 
200
        C{accept(2)} fails with C{ECONNABORTED}.
 
201
 
 
202
        It is not clear whether this is actually possible for TCP
 
203
        connections on modern versions of Linux.
 
204
        """
 
205
        return self._acceptFailureTest(ECONNABORTED)
 
206
 
 
207
 
 
208
    def test_noFilesFromAccept(self):
 
209
        """
 
210
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
 
211
        C{accept(2)} fails with C{ENFILE}.
 
212
 
 
213
        This can occur on Linux when the system has exhausted (!) its supply
 
214
        of inodes.
 
215
        """
 
216
        return self._acceptFailureTest(ENFILE)
 
217
    if platform.getType() == 'win32':
 
218
        test_noFilesFromAccept.skip = "Windows accept(2) cannot generate ENFILE"
 
219
 
 
220
 
 
221
    def test_noMemoryFromAccept(self):
 
222
        """
 
223
        Similar to L{test_tooManyFilesFromAccept}, but test the case where
 
224
        C{accept(2)} fails with C{ENOMEM}.
 
225
 
 
226
        On Linux at least, this can sensibly occur, even in a Python program
 
227
        (which eats memory like no ones business), when memory has become
 
228
        fragmented or low memory has been filled (d_alloc calls
 
229
        kmem_cache_alloc calls kmalloc - kmalloc only allocates out of low
 
230
        memory).
 
231
        """
 
232
        return self._acceptFailureTest(ENOMEM)
 
233
    if platform.getType() == 'win32':
 
234
        test_noMemoryFromAccept.skip = "Windows accept(2) cannot generate ENOMEM"
 
235
 
 
236
if not interfaces.IReactorFDSet.providedBy(reactor):
 
237
    skipMsg = 'This test only applies to reactors that implement IReactorFDset'
 
238
    PlatformAssumptionsTestCase.skip = skipMsg
 
239
    SelectReactorTestCase.skip = skipMsg
 
240