Add tests around the OSError retry logic

This updates our retry logic to be more specific in catching OSErrors
and it adds specific tests to show that it works and properly
re-initializes the StyleGuide with the pep8.StandardReport class so we
can fall back on serial behaviour gracefully.

Closes #74
This commit is contained in:
Ian Cordasco 2015-08-22 19:44:44 -05:00
parent f1aa58889d
commit 9d734158ca
2 changed files with 89 additions and 4 deletions

View file

@ -120,7 +120,8 @@ class StyleGuide(object):
])
def __init__(self, **kwargs):
self._styleguide = NoQAStyleGuide(**kwargs)
# This allows us to inject a mocked StyleGuide in the tests.
self._styleguide = kwargs.pop('styleguide', NoQAStyleGuide(**kwargs))
@property
def options(self):
@ -139,8 +140,8 @@ class StyleGuide(object):
"""
try:
return func(*args, **kwargs)
except IOError as ioerr:
if ioerr.errno in self.serial_retry_errors:
except OSError as oserr:
if oserr.errno in self.serial_retry_errors:
self.init_report(pep8.StandardReport)
else:
raise
@ -161,7 +162,7 @@ class StyleGuide(object):
filename=filename,
lines=lines,
expected=expected,
line_offset=0,
line_offset=line_offset,
)

View file

@ -1,5 +1,6 @@
from __future__ import with_statement
import errno
import unittest
try:
from unittest import mock
@ -7,6 +8,7 @@ except ImportError:
import mock # < PY33
from flake8 import engine, util, __version__, reporter
import pep8
class TestEngine(unittest.TestCase):
@ -112,5 +114,87 @@ class TestEngine(unittest.TestCase):
assert 'X' not in sg.options.ignore
def oserror_generator(error_number, message='Ominous OSError message'):
def oserror_side_effect(*args, **kwargs):
if hasattr(oserror_side_effect, 'used'):
return
oserror_side_effect.used = True
raise OSError(error_number, message)
return oserror_side_effect
class TestStyleGuide(unittest.TestCase):
def setUp(self):
mocked_styleguide = mock.Mock(spec=engine.NoQAStyleGuide)
self.styleguide = engine.StyleGuide(styleguide=mocked_styleguide)
self.mocked_sg = mocked_styleguide
def test_proxies_excluded(self):
self.styleguide.excluded('file.py', parent='.')
self.mocked_sg.excluded.assert_called_once_with('file.py', parent='.')
def test_proxies_init_report(self):
reporter = object()
self.styleguide.init_report(reporter)
self.mocked_sg.init_report.assert_called_once_with(reporter)
def test_proxies_check_files(self):
self.styleguide.check_files(['foo', 'bar'])
self.mocked_sg.check_files.assert_called_once_with(
paths=['foo', 'bar']
)
def test_proxies_input_file(self):
self.styleguide.input_file('file.py',
lines=[9, 10],
expected='foo',
line_offset=20)
self.mocked_sg.input_file.assert_called_once_with(filename='file.py',
lines=[9, 10],
expected='foo',
line_offset=20)
def test_check_files_retries_on_specific_OSErrors(self):
self.mocked_sg.check_files.side_effect = oserror_generator(
errno.ENOSPC, 'No space left on device'
)
self.styleguide.check_files(['foo', 'bar'])
self.mocked_sg.init_report.assert_called_once_with(pep8.StandardReport)
def test_input_file_retries_on_specific_OSErrors(self):
self.mocked_sg.input_file.side_effect = oserror_generator(
errno.ENOSPC, 'No space left on device'
)
self.styleguide.input_file('file.py')
self.mocked_sg.init_report.assert_called_once_with(pep8.StandardReport)
def test_check_files_reraises_unknown_OSErrors(self):
self.mocked_sg.check_files.side_effect = oserror_generator(
errno.EADDRINUSE,
'lol why are we talking about binding to sockets'
)
self.assertRaises(OSError, self.styleguide.check_files,
['foo', 'bar'])
def test_input_file_reraises_unknown_OSErrors(self):
self.mocked_sg.input_file.side_effect = oserror_generator(
errno.EADDRINUSE,
'lol why are we talking about binding to sockets'
)
self.assertRaises(OSError, self.styleguide.input_file,
['foo', 'bar'])
if __name__ == '__main__':
unittest.main()