Skip to content

Commit

Permalink
Convert test_rand to use pytest-style tests (pyca#563)
Browse files Browse the repository at this point in the history
Fix up the assert helpers, subclass form `object` rather than test case,
and use parametrization where appropriate.

One helper method on the original `TestCase` was the ability to create
temporary directories that were cleaned up at the end of the test --
now we use a pytest fixture instead: http://doc.pytest.org/en/latest/tmpdir.html

Addresses pyca#340.
  • Loading branch information
alexwlchan authored and hynek committed Oct 24, 2016
1 parent 29add1d commit 6b69c55
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 97 deletions.
2 changes: 1 addition & 1 deletion src/OpenSSL/rand.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def status():
"""
Check whether the PRNG has been seeded with enough data.
:return: :obj:`True` if the PRNG is seeded enough, :obj:`False` otherwise.
:return: 1 if the PRNG is seeded enough, 0 otherwise.
"""
return _lib.RAND_status()

Expand Down
14 changes: 14 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Copyright (c) The pyOpenSSL developers
# See LICENSE for details.

from tempfile import mktemp

import pytest


def pytest_report_header(config):
import OpenSSL.SSL
Expand All @@ -10,3 +14,13 @@ def pytest_report_header(config):
openssl=OpenSSL.SSL.SSLeay_version(OpenSSL.SSL.SSLEAY_VERSION),
cryptography=cryptography.__version__
)


@pytest.fixture
def tmpfile(tmpdir):
"""
Return UTF-8-encoded bytes of a path to a tmp file.
The file will be cleaned up after the test run.
"""
return mktemp(dir=tmpdir.dirname).encode("utf-8")
177 changes: 82 additions & 95 deletions tests/test_rand.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# See LICENSE for details.

"""
Unit tests for :py:obj:`OpenSSL.rand`.
Unit tests for `OpenSSL.rand`.
"""

import os
Expand All @@ -13,146 +13,133 @@

from OpenSSL import rand

from .util import NON_ASCII, TestCase
from .util import NON_ASCII


class RandTests(TestCase):
def test_bytes_wrong_args(self):
class TestRand(object):

@pytest.mark.parametrize('args', [
(None),
(b"foo"),
])
def test_bytes_wrong_args(self, args):
"""
:py:obj:`OpenSSL.rand.bytes` raises :py:obj:`TypeError` if called with
the wrong number of arguments or with a non-:py:obj:`int` argument.
`OpenSSL.rand.bytes` raises `TypeError` if called with a non-`int`
argument.
"""
self.assertRaises(TypeError, rand.bytes)
self.assertRaises(TypeError, rand.bytes, None)
self.assertRaises(TypeError, rand.bytes, 3, None)
with pytest.raises(TypeError):
rand.bytes(*args)

def test_insufficientMemory(self):
def test_insufficient_memory(self):
"""
:py:obj:`OpenSSL.rand.bytes` raises :py:obj:`MemoryError` if more bytes
are requested than will fit in memory.
`OpenSSL.rand.bytes` raises `MemoryError` if more bytes are requested
than will fit in memory.
"""
self.assertRaises(MemoryError, rand.bytes, sys.maxsize)
with pytest.raises(MemoryError):
rand.bytes(sys.maxsize)

def test_bytes(self):
"""
Verify that we can obtain bytes from rand_bytes() and
that they are different each time. Test the parameter
of rand_bytes() for bad values.
Verify that we can obtain bytes from rand_bytes() and that they are
different each time. Test the parameter of rand_bytes() for
bad values.
"""
b1 = rand.bytes(50)
self.assertEqual(len(b1), 50)
assert len(b1) == 50
b2 = rand.bytes(num_bytes=50) # parameter by name
self.assertNotEqual(b1, b2) # Hip, Hip, Horay! FIPS complaince
assert b1 != b2 # Hip, Hip, Horay! FIPS complaince
b3 = rand.bytes(num_bytes=0)
self.assertEqual(len(b3), 0)
exc = self.assertRaises(ValueError, rand.bytes, -1)
self.assertEqual(str(exc), "num_bytes must not be negative")
assert len(b3) == 0
with pytest.raises(ValueError) as exc:
rand.bytes(-1)
assert str(exc.value) == "num_bytes must not be negative"

def test_add_wrong_args(self):
@pytest.mark.parametrize('args', [
(b"foo", None),
(None, 3),
])
def test_add_wrong_args(self, args):
"""
When called with the wrong number of arguments, or with arguments not
of type :py:obj:`str` and :py:obj:`int`, :py:obj:`OpenSSL.rand.add`
raises :py:obj:`TypeError`.
`OpenSSL.rand.add` raises `TypeError` if called with arguments not of
type `str` and `int`.
"""
self.assertRaises(TypeError, rand.add)
self.assertRaises(TypeError, rand.add, b"foo", None)
self.assertRaises(TypeError, rand.add, None, 3)
self.assertRaises(TypeError, rand.add, b"foo", 3, None)
with pytest.raises(TypeError):
rand.add(*args)

def test_add(self):
"""
:py:obj:`OpenSSL.rand.add` adds entropy to the PRNG.
`OpenSSL.rand.add` adds entropy to the PRNG.
"""
rand.add(b'hamburger', 3)

def test_seed_wrong_args(self):
@pytest.mark.parametrize('args', [
(None),
(42),
])
def test_seed_wrong_args(self, args):
"""
When called with the wrong number of arguments, or with
a non-:py:obj:`str` argument, :py:obj:`OpenSSL.rand.seed` raises
:py:obj:`TypeError`.
`OpenSSL.rand.seed` raises `TypeError` if called with
a non-`str` argument.
"""
self.assertRaises(TypeError, rand.seed)
self.assertRaises(TypeError, rand.seed, None)
self.assertRaises(TypeError, rand.seed, b"foo", None)
with pytest.raises(TypeError):
rand.seed(*args)

def test_seed(self):
"""
:py:obj:`OpenSSL.rand.seed` adds entropy to the PRNG.
`OpenSSL.rand.seed` adds entropy to the PRNG.
"""
rand.seed(b'milk shake')

def test_status_wrong_args(self):
"""
:py:obj:`OpenSSL.rand.status` raises :py:obj:`TypeError` when called
with any arguments.
"""
self.assertRaises(TypeError, rand.status, None)

def test_status(self):
"""
:py:obj:`OpenSSL.rand.status` returns :py:obj:`True` if the PRNG has
sufficient entropy, :py:obj:`False` otherwise.
`OpenSSL.rand.status` returns `1` if the PRNG has sufficient entropy,
`0` otherwise.
"""
# It's hard to know what it is actually going to return. Different
# OpenSSL random engines decide differently whether they have enough
# entropy or not.
self.assertTrue(rand.status() in (1, 2))
assert rand.status() in (0, 1)

def test_egd_warning(self):
@pytest.mark.parametrize('args', [
(b"foo", 255),
(b"foo",),
])
def test_egd_warning(self, args):
"""
Calling egd raises :exc:`DeprecationWarning`.
"""
pytest.deprecated_call(rand.egd, b"foo", 255)
pytest.deprecated_call(rand.egd, b"foo")

def test_egd_wrong_args(self):
"""
:meth:`OpenSSL.rand.egd` raises :exc:`TypeError` when called with the
wrong number of arguments or with arguments not of type :obj:`str` and
:obj:`int`.
"""
for args in [(),
(None,),
("foo", None),
(None, 3),
("foo", 3, None)]:
with pytest.raises(TypeError):
rand.egd(*args)

def test_cleanup_wrong_args(self):
"""
:py:obj:`OpenSSL.rand.cleanup` raises :py:obj:`TypeError` when called
with any arguments.
"""
self.assertRaises(TypeError, rand.cleanup, None)
pytest.deprecated_call(rand.egd, *args)

def test_cleanup(self):
"""
:py:obj:`OpenSSL.rand.cleanup` releases the memory used by the PRNG and
returns :py:obj:`None`.
`OpenSSL.rand.cleanup` releases the memory used by the PRNG and
returns `None`.
"""
self.assertIdentical(rand.cleanup(), None)
assert rand.cleanup() is None

def test_load_file_wrong_args(self):
@pytest.mark.parametrize('args', [
("foo", None),
(None, 1),
])
def test_load_file_wrong_args(self, args):
"""
:py:obj:`OpenSSL.rand.load_file` raises :py:obj:`TypeError` when called
the wrong number of arguments or arguments not of type :py:obj:`str`
and :py:obj:`int`.
`OpenSSL.rand.load_file` raises `TypeError` when with arguments
not of type `str` and `int`.
"""
self.assertRaises(TypeError, rand.load_file)
self.assertRaises(TypeError, rand.load_file, "foo", None)
self.assertRaises(TypeError, rand.load_file, None, 1)
self.assertRaises(TypeError, rand.load_file, "foo", 1, None)
with pytest.raises(TypeError):
rand.load_file(*args)

def test_write_file_wrong_args(self):
@pytest.mark.parametrize('args', [
None,
1,
])
def test_write_file_wrong_args(self, args):
"""
:py:obj:`OpenSSL.rand.write_file` raises :py:obj:`TypeError` when
called with the wrong number of arguments or a non-:py:obj:`str`
argument.
`OpenSSL.rand.write_file` raises `TypeError` when called with
a non-`str` argument.
"""
self.assertRaises(TypeError, rand.write_file)
self.assertRaises(TypeError, rand.write_file, None)
self.assertRaises(TypeError, rand.write_file, "foo", None)
with pytest.raises(TypeError):
rand.write_file(*args)

def _read_write_test(self, path):
"""
Expand All @@ -168,7 +155,7 @@ def _read_write_test(self, path):

# Verify length of written file
size = os.stat(path)[stat.ST_SIZE]
self.assertEqual(1024, size)
assert size == 1024

# Read random bytes from file
rand.load_file(path)
Expand All @@ -177,19 +164,19 @@ def _read_write_test(self, path):
# Cleanup
os.unlink(path)

def test_bytes_paths(self):
def test_bytes_paths(self, tmpfile):
"""
Random data can be saved and loaded to files with paths specified as
bytes.
"""
path = self.mktemp()
path = tmpfile
path += NON_ASCII.encode(sys.getfilesystemencoding())
self._read_write_test(path)

def test_unicode_paths(self):
def test_unicode_paths(self, tmpfile):
"""
Random data can be saved and loaded to files with paths specified as
unicode.
"""
path = self.mktemp().decode('utf-8') + NON_ASCII
path = tmpfile.decode('utf-8') + NON_ASCII
self._read_write_test(path)
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ setenv =
PIP_NO_BINARY=cryptography
commands =
openssl version
coverage run --parallel -m pytest {posargs}
coverage run --parallel -m pytest -v {posargs}

[testenv:py27-twistedMaster]
deps =
Expand Down

0 comments on commit 6b69c55

Please sign in to comment.