passio: Implement exercise (#2195)

This commit is contained in:
AnAccountForReportingBugs
2020-10-12 09:26:43 -05:00
committed by GitHub
parent a050731e19
commit f3e5532e13
5 changed files with 1135 additions and 0 deletions

View File

@@ -1482,6 +1482,18 @@
"loops"
]
},
{
"slug": "paasio",
"uuid": "ec36f9ed-f376-43d9-90ee-be0bea0cfc98",
"core": false,
"unlocked_by": "book-store",
"difficulty": 5,
"topics": [
"classes",
"interfaces",
"networking"
]
},
{
"slug": "accumulate",
"uuid": "e7351e8e-d3ff-4621-b818-cd55cf05bffd",

View File

@@ -0,0 +1,60 @@
# PaaS I/O
Report network IO statistics.
You are writing a [PaaS][], and you need a way to bill customers based
on network and filesystem usage.
Create a wrapper for network connections and files that can report IO
statistics. The wrapper must report:
- The total number of bytes read/written.
- The total number of read/write operations.
[PaaS]: http://en.wikipedia.org/wiki/Platform_as_a_service
## Exception messages
Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to
indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not
every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include
a message.
To raise a message with an exception, just write it as an argument to the exception type. For example, instead of
`raise Exception`, you should write:
```python
raise Exception("Meaningful message indicating the source of the error")
```
## Running the tests
To run the tests, run `pytest paasio_test.py`
Alternatively, you can tell Python to run the pytest module:
`python -m pytest paasio_test.py`
### Common `pytest` options
- `-v` : enable verbose output
- `-x` : stop running tests on first failure
- `--ff` : run failures from previous test before running other test cases
For other options, see `python -m pytest -h`
## Submitting Exercises
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/paasio` directory.
You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`.
For more detailed information about running tests, code style and linting,
please see [Running the Tests](http://exercism.io/tracks/python/tests).
## Source
Brian Matsuo [https://github.com/bmatsuo](https://github.com/bmatsuo)
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

102
exercises/paasio/example.py Normal file
View File

@@ -0,0 +1,102 @@
import io
class MeteredFile(io.BufferedRandom):
"""Implement using a subclassing model."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._read_bytes = 0
self._read_ops = 0
self._write_bytes = 0
self._write_ops = 0
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
return super().__exit__(exc_type, exc_val, exc_tb)
def __iter__(self):
return self
def __next__(self):
self._read_ops += 1
data = super().readline()
self._read_bytes += len(data)
if data:
return data
raise StopIteration
def read(self, size=-1):
self._read_ops += 1
data = super().read(size)
self._read_bytes += len(data)
return data
@property
def read_bytes(self):
return self._read_bytes
@property
def read_ops(self):
return self._read_ops
def write(self, b):
self._write_ops += 1
length = super().write(b)
self._write_bytes += length
return length
@property
def write_bytes(self):
return self._write_bytes
@property
def write_ops(self):
return self._write_ops
class MeteredSocket:
"""Implement using a delegation model."""
def __init__(self, socket):
self._socket = socket
self._recv_bytes = 0
self._recv_ops = 0
self._send_bytes = 0
self._send_ops = 0
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
return self._socket.__exit__(exc_type, exc_val, exc_tb)
def recv(self, bufsize, flags=0):
self._recv_ops += 1
data = self._socket.recv(bufsize, flags)
self._recv_bytes += len(data)
return data
@property
def recv_bytes(self):
return self._recv_bytes
@property
def recv_ops(self):
return self._recv_ops
def send(self, data, flags=0):
self._send_ops += 1
length = self._socket.send(data, flags)
self._send_bytes += length
return length
@property
def send_bytes(self):
return self._send_bytes
@property
def send_ops(self):
return self._send_ops

View File

@@ -0,0 +1,77 @@
import io
class MeteredFile(io.BufferedRandom):
"""Implement using a subclassing model."""
def __init__(self, *args, **kwargs):
pass
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def __iter__(self):
pass
def __next__(self):
pass
def read(self, size=-1):
pass
@property
def read_bytes(self):
pass
@property
def read_ops(self):
pass
def write(self, b):
pass
@property
def write_bytes(self):
pass
@property
def write_ops(self):
pass
class MeteredSocket:
"""Implement using a delegation model."""
def __init__(self, socket):
pass
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def recv(self, bufsize, flags=0):
pass
@property
def recv_bytes(self):
pass
@property
def recv_ops(self):
pass
def send(self, data, flags=0):
pass
@property
def send_bytes(self):
pass
@property
def send_ops(self):
pass

View File

@@ -0,0 +1,884 @@
import errno
import inspect
import io
import logging
import os
import unittest
from unittest.mock import ANY, call, NonCallableMagicMock, patch
from paasio import MeteredFile, MeteredSocket
LOGGER = logging.getLogger(__name__)
ZEN = b"""Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""
# Implementation tests begin on line 456
# Code below this line consists of mocks and tests for mocks compliance
class MockException(Exception):
pass
class MockFile(io.BytesIO):
def __init__(self, *args, chunk=None, exception=None, **kwargs):
super(MockFile, self).__init__(*args, **kwargs)
self.__chunk = chunk
self.__exception = exception
def __exit__(self, exc_type, exc_val, exc_tb):
ret = super(MockFile, self).__exit__(exc_type, exc_val, exc_tb)
if exc_type is not None and "suppress" in exc_val.args[0]:
return True
return ret
def read(self, size=-1):
if self.__exception is not None:
raise self.__exception
if self.__chunk is None:
return super(MockFile, self).read(size)
if size is None:
return super(MockFile, self).read(self.__chunk)
if size < 0:
return super(MockFile, self).read(self.__chunk)
return super(MockFile, self).read(min(self.__chunk, size))
def write(self, data):
if self.__chunk is None:
return super(MockFile, self).write(data)
return super(MockFile, self).write(data[: self.__chunk])
class MockSock:
def __init__(self, *, chunk=None, exception=None):
self._recver = io.BytesIO(ZEN)
self._sender = io.BytesIO()
self.__closed = False
self.__chunk = chunk
self.__exception = exception
self.flags = None
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._recver.close()
self._sender.close()
self.__closed = True
if exc_type is not None and "suppress" in exc_val.args[0]:
return True
return False
def recv(self, bufsize, flags=0):
if self.__closed:
raise OSError(errno.EBADF, os.strerror(errno.EBADF))
if bufsize is None:
raise TypeError(
"'NoneType' object cannot be interpreted as an integer"
)
if not isinstance(flags, int):
raise TypeError(
"an integer is required (got type {})".format(
type(flags).__name__
)
)
self.flags = flags
if self.__exception is not None:
raise self.__exception
if self.__chunk is None:
return self._recver.read(bufsize)
else:
return self._recver.read(min(self.__chunk, bufsize))
def send(self, data, flags=0):
if self.__closed:
raise OSError(errno.EBADF, os.strerror(errno.EBADF))
if not isinstance(flags, int):
raise TypeError(
"an integer is required (got type {})".format(
type(flags).__name__
)
)
self.flags = flags
if self.__chunk is None:
return self._sender.write(data)
return self._sender.write(data[: self.__chunk])
class SuperMock:
"""Mock for super().__init__ calls only, as mock.MagicMock cannot."""
def __init__(self, *args, **kwargs):
if self.initialized:
self.init_called += 1
else:
self.initialized = True
def __call__(self, *args, **kwargs):
frame = inspect.currentframe()
if frame is None:
raise RuntimeError("Could not get current frame object")
stack = inspect.getouterframes(frame)
if any(
frame[3] == "__init__" and "paasio" in frame[1] for frame in stack
):
return self
else:
return self.mock_object
def __repr__(self):
return "<SuperMock at {} with mock object: {!r}>".format(
hex(id(self)), self.mock_object
)
mock_object = None
init_called = 0
initialized = False
class MockTestCase(unittest.TestCase):
def setUp(self):
self.test_passed = False
def tearDown(self):
if not self.test_passed:
LOGGER.critical(
"\nError in mocks, please report to Exercism: %s", self.id()
)
def test_fixture_exception(self):
self.assertEqual(False, self.test_passed)
with self.assertLogs(__name__, level="CRITICAL") as logs:
self.tearDown()
self.assertEqual(1, len(logs.output))
self.assertRegex(logs.output[0], ":\nError in mocks")
self.assertRegex(logs.output[0], "{}$".format(self.id()))
self.test_passed = True
class SuperMockTest(MockTestCase):
def test_super_mock_repr(self):
mock = SuperMock()
expected = "<SuperMock at {} with mock object: {}>".format(
hex(id(mock)), repr(None)
)
self.assertEqual(expected, repr(mock))
file = MockFile()
mock.mock_object = file
expected = "<SuperMock at {} with mock object: {}>".format(
hex(id(mock)), repr(file)
)
self.assertEqual(expected, repr(mock))
self.test_passed = True
def test_super_mock_init(self):
self.assertIs(False, SuperMock.initialized)
mock = SuperMock()
self.assertIs(True, mock.initialized)
for calls in range(5):
self.assertEqual(calls, mock.init_called)
mock.__init__()
self.test_passed = True
def test_super_mock_call(self):
def call2(obj):
return obj()
def __init__(obj):
ret = call2(obj)
self.assertEqual(obj(), ret)
return ret
mock = SuperMock()
self.assertIs(None, mock.mock_object)
wrapped = object()
mock.mock_object = wrapped
self.assertIs(wrapped, mock())
self.assertIs(mock, __init__(mock))
self.test_passed = True
@patch("inspect.currentframe", return_value=None)
def test_super_mock_frame_error(self, inspect_mock):
with self.assertRaisesRegex(
RuntimeError, "^Could not get current frame object$"
):
SuperMock()()
self.test_passed = True
class MockSockTest(MockTestCase):
def test_context_manager(self):
with MockSock() as socket:
socket.recv(30)
socket.send(b"data")
with self.assertRaisesRegex(OSError, os.strerror(errno.EBADF)):
socket.recv(30)
with self.assertRaisesRegex(OSError, os.strerror(errno.EBADF)):
socket.send(b"")
self.test_passed = True
def test_context_manager_exception_raise(self):
exception = MockException("Should raise")
with self.assertRaisesRegex(MockException, "Should raise") as err:
with MockSock(exception=exception) as socket:
socket.recv(4096)
self.assertEqual(exception, err.exception)
self.test_passed = True
def test_context_manager_exception_suppress(self):
exception = MockException("Should suppress")
with MockSock(exception=exception) as socket:
socket.recv(4096)
self.test_passed = True
def test_recv_once(self):
with MockSock() as socket:
actual_recv = socket.recv(4096)
self.assertEqual(ZEN, actual_recv)
self.test_passed = True
def test_recv_multiple(self):
actual_recv = b""
with MockSock() as socket:
for _ in range(5):
actual_recv += socket.recv(30)
self.assertEqual(ZEN[:150], actual_recv)
self.test_passed = True
def test_recv_multiple_chunk(self):
actual_recv = b""
with MockSock(chunk=20) as socket:
for _ in range(5):
actual_recv += socket.recv(4096)
actual_recv += socket.recv(10)
self.assertEqual(ZEN[:110], actual_recv)
self.test_passed = True
def test_recv_under_size(self):
with MockSock(chunk=257) as socket:
actual_recv = socket.recv(4096)
self.assertEqual(ZEN[:257], actual_recv)
self.test_passed = True
def test_send_once(self):
with MockSock(chunk=257) as socket:
send_len = socket.send(ZEN)
self.assertEqual(ZEN[:257], socket._sender.getbuffer())
self.assertEqual(257, send_len)
self.test_passed = True
def test_send_multiple(self):
send_len = 0
expected = b"Tomorrow's victory is today's practice."
with MockSock() as socket:
send_len += socket.send(b"Tomorro")
send_len += socket.send(b"w's victo")
send_len += socket.send(b"ry is today")
send_len += socket.send(b"'s practice.")
self.assertEqual(expected, socket._sender.getbuffer())
self.assertEqual(39, send_len)
self.test_passed = True
def test_send_under_size(self):
with MockSock(chunk=257) as socket:
send_len = socket.send(ZEN[:123])
self.assertEqual(ZEN[:123], socket._sender.getbuffer())
self.assertEqual(123, send_len)
self.test_passed = True
def test_bufsize_required(self):
with self.assertRaisesRegex(TypeError, "argument"):
with MockSock() as socket:
socket.recv()
with self.assertRaisesRegex(TypeError, "'NoneType'.+integer"):
with MockSock() as socket:
socket.recv(None)
self.test_passed = True
def test_flags_support(self):
with MockSock() as socket:
self.assertEqual(len(ZEN), socket.send(ZEN, 42))
self.assertEqual(ZEN, socket.recv(4096, 24))
with MockSock() as mock_sock:
self.assertIs(None, mock_sock.flags)
mock_sock.recv(50)
self.assertEqual(0, mock_sock.flags)
mock_sock.send(b"no flags")
self.assertEqual(0, mock_sock.flags)
mock_sock.recv(30, 30)
self.assertEqual(30, mock_sock.flags)
mock_sock.send(b"flags", 1024)
self.assertEqual(
1024, mock_sock.flags,
)
with self.assertRaisesRegex(
TypeError, r"^an integer is.+NoneType\)$"
):
mock_sock.send(b"data", None)
with self.assertRaisesRegex(
TypeError, r"^an integer is.+bytes\)$"
):
mock_sock.send(b"data", b"flags")
with self.assertRaisesRegex(
TypeError, r"^an integer is.+NoneType\)$"
):
mock_sock.recv(b"data", None)
with self.assertRaisesRegex(
TypeError, r"^an integer is.+bytes\)$"
):
mock_sock.recv(b"data", b"flags")
self.test_passed = True
class MockFileTest(MockTestCase):
def test_context_manager(self):
with MockFile(ZEN) as file:
file.read()
with self.assertRaisesRegex(
ValueError, "I/O operation on closed file."
):
file.read()
with self.assertRaisesRegex(
ValueError, "I/O operation on closed file."
):
file.write(b"data")
self.test_passed = True
def test_context_manager_exception_raise(self):
exception = MockException("Should raise")
mock = MockFile(ZEN, exception=exception)
with self.assertRaisesRegex(MockException, "Should raise") as err:
with mock as file:
file.read()
self.assertEqual(exception, err.exception)
self.test_passed = True
def test_context_manager_exception_suppress(self):
exception = MockException("Should suppress")
mock = MockFile(ZEN, exception=exception)
with mock as file:
file.read()
self.test_passed = True
def test_read_once(self):
with MockFile(ZEN) as file:
actual_read = file.read()
self.assertEqual(ZEN, actual_read)
with MockFile(ZEN) as file:
actual_read = file.read(None)
self.assertEqual(ZEN, actual_read)
with MockFile(ZEN) as file:
actual_read = file.read(-1)
self.assertEqual(ZEN, actual_read)
self.test_passed = True
def test_read_multiple(self):
actual_read = b""
with MockFile(ZEN) as file:
for _ in range(5):
actual_read += file.read(30)
self.assertEqual(ZEN[:150], actual_read)
self.test_passed = True
def test_read_multiple_chunk(self):
actual_read = b""
with MockFile(ZEN, chunk=20) as file:
for _ in range(5):
actual_read += file.read()
actual_read += file.read(10)
self.assertEqual(ZEN[:110], actual_read)
actual_read = b""
with MockFile(ZEN, chunk=20) as file:
for size in [None, -2, -1, 0, 1, 2]:
actual_read += file.read(size)
actual_read += file.read(10)
self.assertEqual(ZEN[:73], actual_read)
self.test_passed = True
def test_read_under_size(self):
with MockFile(ZEN, chunk=257) as file:
actual_read = file.read()
self.assertEqual(ZEN[:257], actual_read)
self.test_passed = True
def test_write_once(self):
with MockFile(chunk=257) as file:
write_len = file.write(ZEN)
self.assertEqual(ZEN[:257], file.getbuffer())
self.assertEqual(257, write_len)
self.test_passed = True
def test_write_multiple(self):
write_len = 0
expected = b"Tomorrow's victory is today's practice."
with MockFile() as file:
write_len += file.write(b"Tomorro")
write_len += file.write(b"w's victo")
write_len += file.write(b"ry is today")
write_len += file.write(b"'s practice.")
self.assertEqual(expected, file.getbuffer())
self.assertEqual(len(expected), write_len)
self.test_passed = True
def test_write_under_size(self):
with MockFile(chunk=257) as file:
write_len = file.write(ZEN[:123])
self.assertEqual(ZEN[:123], file.getbuffer())
self.assertEqual(123, write_len)
self.test_passed = True
# Tests for paasio.py begin here
class MeteredSocketTest(unittest.TestCase):
def test_context_manager(self):
wrapped = MockSock()
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
mock.__exit__.side_effect = wrapped.__exit__
with MeteredSocket(mock) as socket:
self.assertFalse(mock.__enter__.called)
socket.recv(30)
self.assertFalse(mock.__enter__.called)
mock.__exit__.assert_called_once_with(None, None, None)
self.assertEqual(2, len(mock.mock_calls))
with self.assertRaisesRegex(OSError, os.strerror(errno.EBADF)):
socket.recv(30)
with self.assertRaisesRegex(OSError, os.strerror(errno.EBADF)):
socket.send(b"")
def test_context_manager_exception_raise(self):
exception = MockException("Should raise")
wrapped = MockSock(exception=exception)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
mock.__exit__.side_effect = wrapped.__exit__
with self.assertRaisesRegex(MockException, "Should raise") as err:
with MeteredSocket(mock) as socket:
self.assertFalse(mock.__enter__.called)
socket.recv(4096)
self.assertFalse(mock.__enter__.called)
mock.__exit__.assert_called_once_with(
MockException, err.exception, ANY,
)
self.assertEqual(exception, err.exception)
def test_context_manager_exception_suppress(self):
exception = MockException("Should suppress")
wrapped = MockSock(exception=exception)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
mock.__exit__.side_effect = wrapped.__exit__
with MeteredSocket(mock) as socket:
self.assertFalse(mock.__enter__.called)
socket.recv(4096)
self.assertFalse(mock.__enter__.called)
mock.__exit__.assert_called_once_with(
MockException, exception, ANY,
)
def test_recv_once(self):
mock = NonCallableMagicMock(wraps=MockSock(), autospec=True)
with MeteredSocket(mock) as socket:
actual_recv = socket.recv(4096)
self.assertEqual(ZEN, actual_recv)
self.assertEqual(1, socket.recv_ops)
self.assertEqual(len(ZEN), socket.recv_bytes)
self.assertEqual(1, mock.recv.call_count)
def test_recv_multiple(self):
wrapped = MockSock()
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
actual_recv = b""
with MeteredSocket(mock) as socket:
for _ in range(5):
actual_recv += socket.recv(30)
self.assertEqual(ZEN[:150], actual_recv)
self.assertEqual(5, socket.recv_ops)
self.assertEqual(150, socket.recv_bytes)
self.assertEqual(5, mock.recv.call_count)
def test_recv_multiple_chunk(self):
wrapped = MockSock(chunk=20)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
actual_recv = b""
with MeteredSocket(mock) as socket:
for _ in range(5):
actual_recv += socket.recv(4096)
actual_recv += socket.recv(10)
self.assertEqual(ZEN[:110], actual_recv)
self.assertEqual(6, socket.recv_ops)
self.assertEqual(110, socket.recv_bytes)
self.assertEqual(6, mock.recv.call_count)
def test_recv_under_size(self):
wrapped = MockSock(chunk=257) # largish odd number
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
with MeteredSocket(mock) as socket:
actual_recv = socket.recv(4096)
self.assertEqual(ZEN[:257], actual_recv)
self.assertEqual(1, socket.recv_ops)
self.assertEqual(257, socket.recv_bytes)
self.assertEqual(1, mock.recv.call_count)
def test_send_once(self):
wrapped = MockSock(chunk=257) # largish odd number
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
with MeteredSocket(mock) as socket:
send_len = socket.send(ZEN)
self.assertEqual(ZEN[:257], wrapped._sender.getbuffer())
self.assertEqual(257, send_len)
self.assertEqual(1, socket.send_ops)
self.assertEqual(257, socket.send_bytes)
self.assertEqual(1, mock.send.call_count)
def test_send_multiple(self):
wrapped = MockSock()
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
send_len = 0
expected = b"Tomorrow's victory is today's practice."
with MeteredSocket(mock) as socket:
send_len += socket.send(b"Tomorro")
send_len += socket.send(b"w's victo")
send_len += socket.send(b"ry is today")
send_len += socket.send(b"'s practice.")
self.assertEqual(expected, wrapped._sender.getbuffer())
self.assertEqual(39, send_len)
self.assertEqual(4, socket.send_ops)
self.assertEqual(39, socket.send_bytes)
self.assertEqual(4, mock.send.call_count)
def test_send_under_size(self):
wrapped = MockSock(chunk=257) # largish odd number
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
with MeteredSocket(mock) as socket:
send_len = socket.send(ZEN[:123])
self.assertEqual(ZEN[:123], wrapped._sender.getbuffer())
self.assertEqual(123, send_len)
self.assertEqual(1, socket.send_ops)
self.assertEqual(123, socket.send_bytes)
self.assertEqual(1, mock.send.call_count)
def test_bufsize_required(self):
mock = NonCallableMagicMock(wraps=MockSock(), autospec=True)
with self.assertRaisesRegex(TypeError, "argument"):
with MeteredSocket(mock) as socket:
socket.recv()
self.assertFalse(mock.recv.called)
mock = NonCallableMagicMock(wraps=MockSock(), autospec=True)
with self.assertRaisesRegex(TypeError, "^'NoneType'.+integer$"):
with MeteredSocket(mock) as socket:
socket.recv(None)
self.assertTrue(
call(None) in mock.recv.mock_calls
or call(None, ANY) in mock.recv.mock_calls
)
def test_flags_support(self):
mock = NonCallableMagicMock(wraps=MockSock(), autospec=True)
with MeteredSocket(mock) as socket:
self.assertEqual(len(ZEN), socket.send(ZEN, 42))
self.assertEqual(ZEN, socket.recv(4096, 24))
mock.send.assert_called_once_with(ZEN, 42)
mock.recv.assert_called_once_with(4096, 24)
wrapped = MockSock()
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
with MeteredSocket(mock) as socket:
socket.recv(50)
self.assertEqual(0, wrapped.flags)
socket.send(b"no flags")
self.assertEqual(0, wrapped.flags)
socket.recv(30, 30)
self.assertEqual(30, wrapped.flags)
socket.send(b"flags", 1024)
self.assertEqual(1024, wrapped.flags)
with self.assertRaisesRegex(TypeError, "integer is required"):
socket.send(b"data", None)
with self.assertRaisesRegex(TypeError, "integer is required"):
socket.send(b"data", b"flags")
with self.assertRaisesRegex(TypeError, "integer is required"):
socket.recv(b"data", None)
with self.assertRaisesRegex(TypeError, "integer is required"):
socket.recv(b"data", b"flags")
def test_stats_read_only(self):
mock = NonCallableMagicMock(wraps=MockSock(), autospec=True)
with MeteredSocket(mock) as socket:
self.assertEqual(0, socket.send_ops)
self.assertEqual(0, socket.send_bytes)
self.assertEqual(0, socket.recv_ops)
self.assertEqual(0, socket.recv_bytes)
for _ in range(277):
socket.send(b"b")
socket.send(b"bytes")
for _ in range(257):
socket.recv(1)
socket.recv(2)
self.assertEqual(278, socket.send_ops)
self.assertEqual(282, socket.send_bytes)
self.assertEqual(258, socket.recv_ops)
self.assertEqual(259, socket.recv_bytes)
with self.assertRaisesRegex(AttributeError, "can't set"):
socket.send_ops = 0
with self.assertRaisesRegex(AttributeError, "can't set"):
socket.send_bytes = 0
with self.assertRaisesRegex(AttributeError, "can't set"):
socket.recv_ops = 0
with self.assertRaisesRegex(AttributeError, "can't set"):
socket.recv_bytes = 0
self.assertEqual(278, socket.send_ops)
self.assertEqual(282, socket.send_bytes)
self.assertEqual(258, socket.recv_ops)
self.assertEqual(259, socket.recv_bytes)
@patch("paasio.super", create=True, new_callable=SuperMock)
class MeteredFileTest(unittest.TestCase):
def test_context_manager(self, super_mock):
wrapped = MockFile(ZEN)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
mock.__exit__.side_effect = wrapped.__exit__
super_mock.mock_object = mock
with MeteredFile() as file:
self.assertEqual(1, super_mock.init_called)
self.assertFalse(mock.__enter__.called)
file.read()
self.assertFalse(mock.__enter__.called)
mock.__exit__.assert_called_once_with(None, None, None)
self.assertEqual(2, len(mock.mock_calls))
with self.assertRaisesRegex(
ValueError, "I/O operation on closed file."
):
file.read()
with self.assertRaisesRegex(
ValueError, "I/O operation on closed file."
):
file.write(b"data")
def test_context_manager_exception_raise(self, super_mock):
exception = MockException("Should raise")
wrapped = MockFile(ZEN, exception=exception)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
mock.__exit__.side_effect = wrapped.__exit__
super_mock.mock_object = mock
with self.assertRaisesRegex(MockException, "Should raise") as err:
with MeteredFile() as file:
self.assertFalse(mock.__enter__.called)
file.read()
self.assertFalse(mock.__enter__.called)
mock.__exit__.assert_called_once_with(
MockException, err.exception, ANY,
)
self.assertEqual(exception, err.exception)
def test_context_manager_exception_suppress(self, super_mock):
exception = MockException("Should suppress")
wrapped = MockFile(ZEN, exception=exception)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
mock.__exit__.side_effect = wrapped.__exit__
super_mock.mock_object = mock
with MeteredFile() as file:
self.assertFalse(mock.__enter__.called)
file.read()
self.assertFalse(mock.__enter__.called)
mock.__exit__.assert_called_once_with(
MockException, exception, ANY,
)
def test_iteration(self, super_mock):
mock = NonCallableMagicMock(wraps=MockFile(ZEN), autospec=True)
super_mock.mock_object = mock
actual_reads = b""
file = MeteredFile()
for line in file:
actual_reads += line
self.assertLess(
0, mock.readline.call_count, "File's readline not called"
)
self.assertGreater(
50, mock.readline.call_count, "Possible infinte loop detected"
)
self.assertEqual(file.read_ops, mock.readline.call_count)
self.assertFalse(mock.__iter__.called)
self.assertEqual(len(ZEN), file.read_bytes)
self.assertEqual(ZEN, actual_reads)
def test_read_once(self, super_mock):
mock = NonCallableMagicMock(wraps=MockFile(ZEN), autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
actual_read = file.read()
self.assertEqual(ZEN, actual_read)
self.assertEqual((len(ZEN)), file.read_bytes)
self.assertEqual(1, file.read_ops)
self.assertEqual(mock.read.call_count, file.read_ops)
mock = NonCallableMagicMock(wraps=MockFile(ZEN), autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
actual_read = file.read(None)
self.assertEqual(ZEN, actual_read)
self.assertEqual((len(ZEN)), file.read_bytes)
self.assertEqual(1, file.read_ops)
self.assertEqual(mock.read.call_count, file.read_ops)
mock = NonCallableMagicMock(wraps=MockFile(ZEN), autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
actual_read = file.read(-1)
self.assertEqual(ZEN, actual_read)
self.assertEqual((len(ZEN)), file.read_bytes)
self.assertEqual(1, file.read_ops)
self.assertEqual(mock.read.call_count, file.read_ops)
def test_read_multiple(self, super_mock):
wrapped = MockFile(ZEN)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
actual_read = b""
with MeteredFile() as file:
for _ in range(5):
actual_read += file.read(30)
self.assertEqual(ZEN[:150], actual_read)
self.assertEqual(5, file.read_ops)
self.assertEqual(150, file.read_bytes)
self.assertEqual(5, mock.read.call_count)
def test_read_multiple_chunk(self, super_mock):
wrapped = MockFile(ZEN, chunk=20)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
actual_read = b""
with MeteredFile() as file:
for _ in range(5):
actual_read += file.read()
actual_read += file.read(10)
self.assertEqual(ZEN[:110], actual_read)
self.assertEqual(6, file.read_ops)
self.assertEqual(110, file.read_bytes)
self.assertEqual(6, mock.read.call_count)
wrapped = MockFile(ZEN, chunk=20)
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
actual_read = b""
with MeteredFile() as file:
for size in [None, -2, -1, 0, 1, 2]:
actual_read += file.read(size)
actual_read += file.read(10)
self.assertEqual(ZEN[:73], actual_read)
self.assertEqual(7, file.read_ops)
self.assertEqual(73, file.read_bytes)
self.assertEqual(7, mock.read.call_count)
def test_read_under_size(self, super_mock):
wrapped = MockFile(ZEN, chunk=257) # largish odd number
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
actual_read = file.read()
self.assertEqual(ZEN[:257], actual_read)
self.assertEqual(1, file.read_ops)
self.assertEqual(257, file.read_bytes)
self.assertEqual(1, mock.read.call_count)
def test_write_once(self, super_mock):
wrapped = MockFile(chunk=257) # largish odd number
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
write_len = file.write(ZEN)
self.assertEqual(ZEN[:257], wrapped.getbuffer())
self.assertEqual(257, write_len)
self.assertEqual(1, file.write_ops)
self.assertEqual(257, file.write_bytes)
self.assertEqual(1, mock.write.call_count)
def test_write_multiple(self, super_mock):
wrapped = MockFile()
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
write_len = 0
expected = b"Tomorrow's victory is today's practice."
with MeteredFile() as file:
write_len += file.write(b"Tomorro")
write_len += file.write(b"w's victo")
write_len += file.write(b"ry is today")
write_len += file.write(b"'s practice.")
self.assertEqual(expected, wrapped.getbuffer())
self.assertEqual(39, write_len)
self.assertEqual(4, file.write_ops)
self.assertEqual(39, file.write_bytes)
self.assertEqual(4, mock.write.call_count)
def test_write_under_size(self, super_mock):
wrapped = MockFile(chunk=257) # largish odd number
mock = NonCallableMagicMock(wraps=wrapped, autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
write_len = file.write(ZEN[:123])
self.assertEqual(ZEN[:123], wrapped.getbuffer())
self.assertEqual(123, write_len)
self.assertEqual(1, file.write_ops)
self.assertEqual(123, file.write_bytes)
self.assertEqual(1, mock.write.call_count)
def test_stats_read_only(self, super_mock):
mock = NonCallableMagicMock(wraps=MockFile(ZEN), autospec=True)
super_mock.mock_object = mock
with MeteredFile() as file:
self.assertEqual(0, file.read_ops)
self.assertEqual(0, file.read_bytes)
for _ in range(57):
file.read(1)
file.read(2)
self.assertEqual(58, file.read_ops)
self.assertEqual(59, file.read_bytes)
self.assertEqual(0, file.write_ops)
self.assertEqual(0, file.write_bytes)
for _ in range(77):
file.write(b"b")
file.write(b"bytes")
self.assertEqual(78, file.write_ops)
self.assertEqual(82, file.write_bytes)
with self.assertRaisesRegex(AttributeError, "can't set"):
file.write_ops = 0
with self.assertRaisesRegex(AttributeError, "can't set"):
file.write_bytes = 0
with self.assertRaisesRegex(AttributeError, "can't set"):
file.read_ops = 0
with self.assertRaisesRegex(AttributeError, "can't set"):
file.read_bytes = 0
self.assertEqual(78, file.write_ops)
self.assertEqual(82, file.write_bytes)
self.assertEqual(58, file.read_ops)
self.assertEqual(59, file.read_bytes)
if __name__ == "__main__":
unittest.main()