Skip to content

Commit c682945

Browse files
committed
refactor ConstructorTestCase
1 parent 412f5c2 commit c682945

File tree

1 file changed

+173
-64
lines changed

1 file changed

+173
-64
lines changed

Lib/test/test_hmac.py

Lines changed: 173 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,19 @@ class CreatorMixin:
6161
def hmac_new(self, key, msg=None, digestmod=None):
6262
raise NotImplementedError
6363

64+
def bind_hmac_new(self, digestmod):
65+
return functools.partial(self.hmac_new, digestmod=digestmod)
66+
6467

6568
class DigestMixin:
6669
"""Mixin exposing a method computing a HMAC digest."""
6770

6871
def hmac_digest(self, key, msg=None, digestmod=None):
6972
raise NotImplementedError
7073

74+
def bind_hmac_digest(self, digestmod):
75+
return functools.partial(self.hmac_digest, digestmod=digestmod)
76+
7177

7278
class ThroughObjectMixin(ModuleMixin, CreatorMixin, DigestMixin):
7379
"""Mixin delegating to hmac.HMAC() and hmac.HMAC(...).digest()."""
@@ -533,82 +539,185 @@ def func(self, *, __name=name): # __name needed to bind 'name'
533539
setattr(cls, name, func)
534540

535541

536-
class ConstructorTestCase(unittest.TestCase):
537-
expected = (
538-
"6c845b47f52b3b47f6590c502db7825aad757bf4fadc8fa972f7cd2e76a5bdeb"
539-
)
542+
class DigestModTestCaseMixin(CreatorMixin, DigestMixin):
540543

541-
@hashlib_helper.requires_hashdigest('sha256')
542-
def test_normal(self):
543-
# Standard constructor call.
544-
try:
545-
hmac.HMAC(b"key", digestmod='sha256')
546-
except Exception:
547-
self.fail("Standard constructor call raised exception.")
544+
def make_digestmod_cases(self, func, digestmods):
545+
return [
546+
*[
547+
(func, args, dict(digestmod=digestmod))
548+
for args in [(self.key,), (self.key, self.msg)]
549+
for digestmod in digestmods
550+
],
551+
*[
552+
(func, (self.key,), dict(msg=self.msg, digestmod=digestmod))
553+
for digestmod in digestmods
554+
],
555+
]
548556

549-
@hashlib_helper.requires_hashdigest('sha256')
550-
def test_with_str_key(self):
551-
# Pass a key of type str, which is an error, because it expects a key
552-
# of type bytes
553-
with self.assertRaises(TypeError):
554-
h = hmac.HMAC("key", digestmod='sha256')
557+
def assert_raises_missing_digestmod(self):
558+
return self.assertRaisesRegex(TypeError, "Missing required.*digestmod")
555559

556-
@hashlib_helper.requires_hashdigest('sha256')
557-
def test_dot_new_with_str_key(self):
558-
# Pass a key of type str, which is an error, because it expects a key
559-
# of type bytes
560-
with self.assertRaises(TypeError):
561-
h = hmac.new("key", digestmod='sha256')
560+
def assert_raises_invalid_digestmod(self):
561+
return self.assertRaisesRegex(ValueError, "[Uu]nsupported.*")
562562

563-
@hashlib_helper.requires_hashdigest('sha256')
564-
def test_withtext(self):
565-
# Constructor call with text.
566-
try:
567-
h = hmac.HMAC(b"key", b"hash this!", digestmod='sha256')
568-
except Exception:
569-
self.fail("Constructor call with text argument raised exception.")
570-
self.assertEqual(h.hexdigest(), self.expected)
563+
def test_constructor_missing_digestmod(self):
564+
catcher = self.assert_raises_missing_digestmod
565+
self.do_test_constructor_missing_digestmod(catcher)
571566

572-
@hashlib_helper.requires_hashdigest('sha256')
573-
def test_with_bytearray(self):
574-
try:
575-
h = hmac.HMAC(bytearray(b"key"), bytearray(b"hash this!"),
576-
digestmod="sha256")
577-
except Exception:
578-
self.fail("Constructor call with bytearray arguments raised exception.")
579-
self.assertEqual(h.hexdigest(), self.expected)
567+
def test_constructor_invalid_digestmod(self):
568+
catcher = self.assert_raises_invalid_digestmod
569+
self.do_test_constructor_invalid_digestmod(catcher)
580570

581-
@hashlib_helper.requires_hashdigest('sha256')
582-
def test_with_memoryview_msg(self):
583-
try:
584-
h = hmac.HMAC(b"key", memoryview(b"hash this!"), digestmod="sha256")
585-
except Exception:
586-
self.fail("Constructor call with memoryview msg raised exception.")
587-
self.assertEqual(h.hexdigest(), self.expected)
571+
def do_test_constructor_missing_digestmod(self, catcher):
572+
for func, args, kwds in self.cases_missing_digestmod_in_constructor():
573+
with self.subTest(args=args, kwds=kwds), catcher():
574+
func(*args, **kwds)
588575

589-
@hashlib_helper.requires_hashdigest('sha256')
590-
def test_withmodule(self):
591-
# Constructor call with text and digest module.
592-
try:
593-
h = hmac.HMAC(b"key", b"", hashlib.sha256)
594-
except Exception:
595-
self.fail("Constructor call with hashlib.sha256 raised exception.")
576+
def do_test_constructor_invalid_digestmod(self, catcher):
577+
for func, args, kwds in self.cases_invalid_digestmod_in_constructor():
578+
with self.subTest(args=args, kwds=kwds), catcher():
579+
func(*args, **kwds)
580+
581+
def cases_missing_digestmod_in_constructor(self):
582+
raise NotImplementedError
583+
584+
def cases_invalid_digestmod_in_constructor(self):
585+
raise NotImplementedError
586+
587+
588+
class ConstructorTestCaseMixin(CreatorMixin, DigestMixin, CheckerMixin):
589+
"""HMAC constructor tests based on HMAC-SHA-2/256."""
590+
591+
key = b"key"
592+
msg = b"hash this!"
593+
res = "6c845b47f52b3b47f6590c502db7825aad757bf4fadc8fa972f7cd2e76a5bdeb"
594+
595+
def do_test_constructor(self, hmac_on_key_and_msg):
596+
self.do_test_constructor_invalid_types(hmac_on_key_and_msg)
597+
self.do_test_constructor_supported_types(hmac_on_key_and_msg)
598+
599+
def do_test_constructor_invalid_types(self, hmac_on_key_and_msg):
600+
self.assertRaises(TypeError, hmac_on_key_and_msg, 1)
601+
self.assertRaises(TypeError, hmac_on_key_and_msg, "key")
602+
603+
self.assertRaises(TypeError, hmac_on_key_and_msg, b"key", 1)
604+
self.assertRaises(TypeError, hmac_on_key_and_msg, b"key", "msg")
605+
606+
def do_test_constructor_supported_types(self, hmac_on_key_and_msg):
607+
for tp_key in [bytes, bytearray]:
608+
for tp_msg in [bytes, bytearray, memoryview]:
609+
with self.subTest(tp_key=tp_key, tp_msg=tp_msg):
610+
h = hmac_on_key_and_msg(tp_key(self.key), tp_msg(self.msg))
611+
self.assertEqual(h.name, "hmac-sha256")
612+
self.assertEqual(h.hexdigest(), self.res)
613+
614+
@hashlib_helper.requires_hashdigest("sha256")
615+
def test_constructor(self):
616+
self.do_test_constructor(self.bind_hmac_new("sha256"))
617+
618+
@hashlib_helper.requires_hashdigest("sha256")
619+
def test_digest(self):
620+
digest = self.hmac_digest(self.key, self.msg, "sha256")
621+
self.assertEqual(digest, binascii.unhexlify(self.res))
622+
623+
624+
class PyConstructorBaseMixin(PyModuleMixin,
625+
DigestModTestCaseMixin,
626+
ConstructorTestCaseMixin):
627+
628+
def cases_missing_digestmod_in_constructor(self):
629+
func, key, msg = self.hmac_new, b'key', b'msg'
630+
cases = self.make_digestmod_cases(func, ['', None, False])
631+
return [*cases, (func, (key,), {}), (func, (key, msg), {})]
632+
633+
def cases_invalid_digestmod_in_constructor(self):
634+
return self.make_digestmod_cases(self.hmac_new, ['unknown'])
635+
636+
@requires_builtin_sha2()
637+
def test_constructor_with_module(self):
638+
self.do_test_constructor(self.bind_hmac_new(sha2.sha256))
639+
640+
@requires_builtin_sha2()
641+
def test_digest_with_module(self):
642+
digest = self.hmac_digest(self.key, self.msg, sha2.sha256)
643+
self.assertEqual(digest, binascii.unhexlify(self.res))
644+
645+
646+
class PyConstructorTestCase(ThroughObjectMixin, PyConstructorBaseMixin,
647+
unittest.TestCase):
648+
"""Test the hmac.HMAC() pure Python constructor."""
649+
650+
651+
class PyModuleConstructorTestCase(ThroughModuleAPIMixin, PyConstructorBaseMixin,
652+
unittest.TestCase):
653+
"""Test the hmac.new() constructor function."""
654+
655+
def test_hmac_digest_digestmod_parameter(self):
656+
func = self.hmac_digest
657+
658+
def raiser():
659+
raise RuntimeError("custom exception")
660+
661+
with self.assertRaisesRegex(RuntimeError, "custom exception"):
662+
func(b'key', b'msg', raiser)
663+
664+
with self.assertRaisesRegex(ValueError, 'hash type'):
665+
func(b'key', b'msg', 'unknown')
666+
667+
with self.assertRaisesRegex(AttributeError, 'new'):
668+
func(b'key', b'msg', 1234)
669+
with self.assertRaisesRegex(AttributeError, 'new'):
670+
func(b'key', b'msg', None)
671+
672+
673+
class ExtensionConstructorTestCaseMixin(DigestModTestCaseMixin,
674+
ConstructorTestCaseMixin):
675+
obj_type = None
676+
exc_type = None
596677

597-
@hashlib_helper.requires_hashlib()
598678
def test_internal_types(self):
599-
# internal C types like are not constructable
600-
check_disallow_instantiation(self, _hashlib.HMAC)
679+
# internal C types are immutable and cannot be instantiated
680+
check_disallow_instantiation(self, self.obj_type)
601681
with self.assertRaisesRegex(TypeError, "immutable type"):
602-
_hashlib.HMAC.value = None
682+
self.obj_type.value = None
603683

604-
@requires_builtin_sha2()
605-
def test_with_sha2(self):
606-
h = hmac.HMAC(b"key", b"hash this!", digestmod=sha2.sha256)
607-
self.assertEqual(h.hexdigest(), self.expected)
608-
self.assertEqual(h.name, "hmac-sha256")
684+
def assert_digestmod_error(self):
685+
self.assertIsSubclass(self.exc_type, ValueError)
686+
return self.assertRaises(self.exc_type)
687+
688+
def test_constructor_missing_digestmod(self):
689+
self.do_test_constructor_missing_digestmod(self.assert_digestmod_error)
690+
691+
def test_constructor_invalid_digestmod(self):
692+
self.do_test_constructor_invalid_digestmod(self.assert_digestmod_error)
693+
694+
def cases_missing_digestmod_in_constructor(self):
695+
func, key, msg = self.hmac_new, b'key', b'msg'
696+
cases = self.make_digestmod_cases(func, ['', None, False])
697+
return [*cases, (func, (key,), {}), (func, (key, msg), {})]
698+
699+
def cases_invalid_digestmod_in_constructor(self):
700+
return self.make_digestmod_cases(self.hmac_new, ['unknown', 1234])
701+
702+
703+
class OpenSSLConstructorTestCase(ThroughOpenSSLAPIMixin,
704+
ExtensionConstructorTestCaseMixin,
705+
unittest.TestCase):
706+
707+
@property
708+
def obj_type(self):
709+
return _hashlib.HMAC
710+
711+
@property
712+
def exc_type(self):
713+
return _hashlib.UnsupportedDigestmodError
609714

610-
digest = hmac.digest(b"key", b"hash this!", sha2.sha256)
611-
self.assertEqual(digest, binascii.unhexlify(self.expected))
715+
def test_hmac_digest_digestmod_parameter(self):
716+
# TODO(picnixz): remove default arguments in _hashlib.hmac_digest()
717+
# since the return value is not a HMAC object but a bytes object.
718+
for value in [object, 'unknown', 1234, None]:
719+
with self.subTest(value=value), self.assert_digestmod_error():
720+
self.hmac_digest(b'key', b'msg', value)
612721

613722

614723
class SanityTestCaseMixin(CreatorMixin):

0 commit comments

Comments
 (0)