Skip to content
This repository has been archived by the owner on Mar 29, 2022. It is now read-only.

Commit

Permalink
Require "100%" statement coverage in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vfaronov committed Aug 2, 2016
1 parent 5d9c4ce commit 7a6425c
Show file tree
Hide file tree
Showing 25 changed files with 188 additions and 75 deletions.
7 changes: 7 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[report]

exclude_lines =
pragma: no cover
def __repr__
raise NotImplementedError
if __name__ == '__main__':
11 changes: 11 additions & 0 deletions HACKING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,14 @@ For example:
- ``None``, which means "don't know"

(this is why you see comparisons to ``False`` all over the place).


Code coverage
-------------
The delivery pipeline (Travis CI) requires "100%" statement coverage in tests.
This is *not* meant to enforce that every line has been tested,
but rather to ensure that no line has been *forgotten*.
Feel free to add ``pragma: no cover`` to code
that would be hard to cover with a natural, functional test.

To run tests with coverage checks locally, use ``tools/pytest_all.sh``.
10 changes: 5 additions & 5 deletions httpolice/citation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ def __init__(self, title, url):
self.title = title
self.url = url

def __eq__(self, other):
def __eq__(self, other): # pragma: no cover
return isinstance(other, Citation) and \
self.title == other.title and self.url == other.url

def __ne__(self, other):
def __ne__(self, other): # pragma: no cover
return not self == other

def subset_of(self, other):
def subset_of(self, other): # pragma: no cover
return self == other

def __hash__(self):
def __hash__(self): # pragma: no cover
return hash((self.title, self.url))


Expand Down Expand Up @@ -78,7 +78,7 @@ def parse_sect(s):
return tuple(int(part) if part.isdigit() else part
for part in s.split('.'))

def subset_of(self, other):
def subset_of(self, other): # pragma: no cover
if self.num != other.num:
return False
if other.section:
Expand Down
4 changes: 2 additions & 2 deletions httpolice/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ def generate_exchanges():
return 0


def excepthook(_type, exc, _traceback):
def excepthook(_type, exc, _traceback): # pragma: no cover
sys.stderr.write('httpolice: unhandled exception: %r\n' % exc)


def main():
def main(): # pragma: no cover
args = parse_args(sys.argv)
if not args.full_traceback:
sys.excepthook = excepthook
Expand Down
15 changes: 3 additions & 12 deletions httpolice/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,6 @@ def enumerate(self, name=None):
def clearly(self, predicate):
return set(name for name in self.names if predicate(name))

def possibly(self, predicate):
return set(name for name in self.names if predicate(name) != False)


class HeaderView(object):

Expand Down Expand Up @@ -161,7 +158,7 @@ def total_entries(self):

@property
def entries(self):
if self._entries is None:
if self._entries is None: # pragma: no cover
self._parse()
return self._entries

Expand All @@ -188,18 +185,12 @@ def is_okay(self):
def __bool__(self):
return bool(self.value)

if sys.version_info[0] < 3:
if sys.version_info[0] < 3: # pragma: no cover
__nonzero__ = __bool__

def __iter__(self):
return iter(self.value)

def __len__(self):
return len(self.value)

def __getitem__(self, i):
return self.value[i] # pylint: disable=unsubscriptable-object

def __contains__(self, other):
# Since headers often contain `Parametrized` values,
# it's useful to be able to check membership by the item itself,
Expand Down Expand Up @@ -238,7 +229,7 @@ def __lt__(self, other):
return self._compare(other, operator.lt)

def __le__(self, other):
return self._compare(other, operator.le)
return self._compare(other, operator.le) # pragma: no cover

def __eq__(self, other):
return self._compare(other, operator.eq)
Expand Down
2 changes: 1 addition & 1 deletion httpolice/inputs/har.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _process_request(data, creator, path):
try:
stream = Stream((wtf + u'\r\n').encode('iso-8859-1'))
more_entries = framing1.parse_header_fields(stream)
except (UnicodeError, ParseError):
except (UnicodeError, ParseError): # pragma: no cover
pass
else:
header_entries.extend(more_entries)
Expand Down
6 changes: 3 additions & 3 deletions httpolice/inputs/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

fs_encoding = sys.getfilesystemencoding()

def _decode_path(path):
def _decode_path(path): # pragma: no cover
if isinstance(path, bytes):
return path.decode(fs_encoding, 'replace')
else:
Expand Down Expand Up @@ -163,10 +163,10 @@ def parse_combined(path):
(preamble, rest) = parts1
try:
preamble = preamble.decode('utf-8')
except UnicodeError as exc:
except UnicodeError as exc: # pragma: no cover
six.raise_from(InputError('%s: invalid UTF-8 in preamble' % path), exc)
parts2 = rest.split(b'======== BEGIN OUTBOUND STREAM ========\r\n', 1)
if len(parts2) != 2:
if len(parts2) != 2: # pragma: no cover
raise InputError('%s: bad combined file: no outbound marker' % path)
(inbound_data, outbound_data) = parts2

Expand Down
2 changes: 1 addition & 1 deletion httpolice/known/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __getattr__(self, name):
else:
raise AttributeError(name)

def __getitem__(self, key):
def __getitem__(self, key): # pragma: no cover
return self._by_key[key]

def __iter__(self):
Expand Down
6 changes: 3 additions & 3 deletions httpolice/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ def __init__(self, name=None, citation=None, max_count=None, inner=None):
self.inner = inner
self._rules = None

def group(self):
def group(self): # pragma: no cover
return self

@property
Expand All @@ -313,7 +313,7 @@ def rules(self):
def build_regex(self):
if self.max_count is None:
return self.inner.as_regex() + b'*'
else:
else: # pragma: no cover
return self.inner.as_regex() + (b'{0,%d}' % self.max_count)


Expand Down Expand Up @@ -612,7 +612,7 @@ def __getitem__(self, i):
return self.data[(self.point + i):(self.point + i + 1)]
elif i == len(self.data) - self.point:
return b''
else:
else: # pragma: no cover
raise IndexError(i)

@property
Expand Down
27 changes: 2 additions & 25 deletions httpolice/reports/text.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# -*- coding: utf-8; -*-

import codecs
import re

from singledispatch import singledispatch
import six

from httpolice import notice
from httpolice.reports.common import (expand_error, expand_piece,
find_reason_phrase, resolve_reference)
from httpolice.reports.common import (expand_piece, find_reason_phrase,
resolve_reference)
from httpolice.util.text import (detypographize, ellipsize, printable,
write_if_any)

Expand Down Expand Up @@ -76,29 +75,7 @@ def _text_to_text(text, _):
def _list_to_text(xs, ctx):
return u''.join(_piece_to_text(x, ctx) for x in xs)

@_piece_to_text.register(notice.Paragraph)
def _para_to_text(para, ctx):
return _piece_to_text(para.content, ctx) + u'\n'

@_piece_to_text.register(notice.Cite)
def _cite_to_text(cite, ctx):
quote = cite.content
if quote:
quote = re.sub(u'\\s+', u' ', _piece_to_text(quote, ctx)).strip()
return u'“%s” (%s)' % (quote, cite.info)
else:
return six.text_type(cite.info)

@_piece_to_text.register(notice.Var)
def _var_to_text(var, ctx):
target = resolve_reference(ctx, var.reference)
return _piece_to_text(target, ctx)

@_piece_to_text.register(notice.ExceptionDetails)
def _exc_to_text(_, ctx):
return u''.join(_piece_to_text(para, ctx) + u'\n'
for para in expand_error(ctx['error']))

@_piece_to_text.register(notice.Ref)
def _ref_to_text(_ref, _ctx):
return u''
4 changes: 2 additions & 2 deletions httpolice/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def transformed_by_proxy(self):
def is_tls(self):
if okay(self.request):
return self.request.is_tls
else:
else: # pragma: no cover
return None


Expand Down Expand Up @@ -743,7 +743,7 @@ def _check_bearer_challenge(resp, hdr, challenge):
# If this is ever extended to proxies, the notices must be adjusted.
# Also note that some text in RFC 6750 only applies to servers
# (where it says "resource server").
if hdr.name != h.www_authenticate:
if hdr.name != h.www_authenticate: # pragma: no cover
return

req = resp.request
Expand Down
17 changes: 7 additions & 10 deletions httpolice/structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __repr__(self):
def __eq__(self, other):
return False

def __hash__(self):
def __hash__(self): # pragma: no cover
return 1

Unavailable = _Unavailable()
Expand All @@ -55,7 +55,7 @@ def __ne__(self, other):
else:
return self.item != other

def __hash__(self):
def __hash__(self): # pragma: no cover
return hash(self.item)


Expand Down Expand Up @@ -84,9 +84,9 @@ def __repr__(self):
def __eq__(self, other):
if isinstance(other, MultiDict):
return self.sequence == other.sequence
return NotImplemented
return NotImplemented # pragma: no cover

def __ne__(self, other):
def __ne__(self, other): # pragma: no cover
return not (self == other)

def __getitem__(self, name):
Expand Down Expand Up @@ -316,13 +316,10 @@ def __eq__(self, other):
else:
return self.code == other

def __ne__(self, other):
if isinstance(other, tuple):
return super(WarningValue, self).__ne__(other)
else:
return self.code != other
def __ne__(self, other): # pragma: no cover
return not (self == other)

def __hash__(self):
def __hash__(self): # pragma: no cover
return hash(self.code)


Expand Down
2 changes: 1 addition & 1 deletion httpolice/syntax/rfc5988.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def link_extension(exclude_builtin):
exclude1 = [name for name in _builtin_params if not name.endswith('*')]
exclude2 = [name.rstrip('*')
for name in _builtin_params if name.endswith('*')]
else:
else: # pragma: no cover
exclude1 = exclude2 = None
return (
(
Expand Down
8 changes: 4 additions & 4 deletions httpolice/util/moves.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
# These are like `six.moves`, but we cannot add them with `six.add_move`
# because we are a library and we must not touch that shared namespace.

try:
try: # pragma: no cover
from email import message_from_bytes
except ImportError: # Python 2
except ImportError: # Python 2; pragma: no cover
from email import message_from_string as message_from_bytes

try:
try: # pragma: no cover
from urllib.parse import unquote_to_bytes
except ImportError: # Python 2
except ImportError: # Python 2; pragma: no cover
from urllib import unquote as unquote_to_bytes
2 changes: 1 addition & 1 deletion httpolice/util/ordered_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import enum


class OrderedEnum(enum.Enum):
class OrderedEnum(enum.Enum): # pragma: no cover

"""An ordered variant of :class:`enum.Enum`.
Expand Down
6 changes: 2 additions & 4 deletions httpolice/util/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,8 @@ def force_bytes(x):


def stdio_as_bytes(f):
if hasattr(f, 'buffer'): # Python 3
return f.buffer
else: # Python 2
return f
# Accommodate Python 2 vs. 3 difference.
return f.buffer if hasattr(f, 'buffer') else f


class WriteIfAny(io.StringIO):
Expand Down
15 changes: 15 additions & 0 deletions test/combined_data/1000_9
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
1000

======== BEGIN INBOUND STREAM ========
GET / HTTP/1.1
Host: example.com
User-Agent: demo

======== BEGIN OUTBOUND STREAM ========
HTTP/1.1 200 OK
Date: Thu, 31 Dec 2015 18:26:56 GMT
Content-Type: text/plain
Content-Length: 14
Content-Encoding: ???

Hello world!
15 changes: 15 additions & 0 deletions test/combined_data/1127_3
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
1127

# Make sure we don't try to parse JSON in 304 responses.

======== BEGIN INBOUND STREAM ========
GET /data/index.json HTTP/1.1
Host: example.com
User-Agent: demo
If-None-Match: W/"foobar123"

======== BEGIN OUTBOUND STREAM ========
HTTP/1.1 304 Not Modified
Date: Thu, 31 Dec 2015 18:26:56 GMT
Content-Type: application/json

15 changes: 15 additions & 0 deletions test/combined_data/alt_svc_clear
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@


======== BEGIN INBOUND STREAM ========
GET / HTTP/1.1
Host: example.com
User-Agent: demo

======== BEGIN OUTBOUND STREAM ========
HTTP/1.1 200 OK
Date: Thu, 31 Dec 2015 18:26:56 GMT
Content-Type: text/plain
Content-Length: 14
Alt-Svc: clear

Hello world!
Binary file added test/combined_data/request_gzip
Binary file not shown.
Loading

0 comments on commit 7a6425c

Please sign in to comment.