diff options
-rw-r--r-- | Lib/test/test_email/__init__.py | 79 | ||||
-rw-r--r-- | Lib/test/test_email/test_generator.py | 41 | ||||
-rw-r--r-- | Lib/test/test_email/test_headerregistry.py | 26 | ||||
-rw-r--r-- | Lib/test/test_email/test_pickleable.py | 69 |
4 files changed, 122 insertions, 93 deletions
diff --git a/Lib/test/test_email/__init__.py b/Lib/test/test_email/__init__.py index 75dc64d..bd9d52c 100644 --- a/Lib/test/test_email/__init__.py +++ b/Lib/test/test_email/__init__.py @@ -71,3 +71,82 @@ class TestEmailBase(unittest.TestCase): for i in range(len(actual)): self.assertIsInstance(actual[i], expected[i], 'item {}'.format(i)) + + +# Metaclass to allow for parameterized tests +class Parameterized(type): + + """Provide a test method parameterization facility. + + Parameters are specified as the value of a class attribute that ends with + the string '_params'. Call the portion before '_params' the prefix. Then + a method to be parameterized must have the same prefix, the string + '_as_', and an arbitrary suffix. + + The value of the _params attribute may be either a dictionary or a list. + The values in the dictionary and the elements of the list may either be + single values, or a list. If single values, they are turned into single + element tuples. However derived, the resulting sequence is passed via + *args to the parameterized test function. + + In a _params dictioanry, the keys become part of the name of the generated + tests. In a _params list, the values in the list are converted into a + string by joining the string values of the elements of the tuple by '_' and + converting any blanks into '_'s, and this become part of the name. The + full name of a generated test is the portion of the _params name before the + '_params' portion, plus an '_', plus the name derived as explained above. + + For example, if we have: + + count_params = range(2) + + def count_as_foo_arg(self, foo): + self.assertEqual(foo+1, myfunc(foo)) + + we will get parameterized test methods named: + test_foo_arg_0 + test_foo_arg_1 + test_foo_arg_2 + + Or we could have: + + example_params = {'foo': ('bar', 1), 'bing': ('bang', 2)} + + def example_as_myfunc_input(self, name, count): + self.assertEqual(name+str(count), myfunc(name, count)) + + and get: + test_myfunc_input_foo + test_myfunc_input_bing + + Note: if and only if the generated test name is a valid identifier can it + be used to select the test individually from the unittest command line. + + """ + + def __new__(meta, classname, bases, classdict): + paramdicts = {} + for name, attr in classdict.items(): + if name.endswith('_params'): + if not hasattr(attr, 'keys'): + d = {} + for x in attr: + if not hasattr(x, '__iter__'): + x = (x,) + n = '_'.join(str(v) for v in x).replace(' ', '_') + d[n] = x + attr = d + paramdicts[name[:-7] + '_as_'] = attr + testfuncs = {} + for name, attr in classdict.items(): + for paramsname, paramsdict in paramdicts.items(): + if name.startswith(paramsname): + testnameroot = 'test_' + name[len(paramsname):] + for paramname, params in paramsdict.items(): + test = (lambda self, name=name, params=params: + getattr(self, name)(*params)) + testname = testnameroot + '_' + paramname + test.__name__ = testname + testfuncs[testname] = test + classdict.update(testfuncs) + return super().__new__(meta, classname, bases, classdict) diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py index c1af024..7e1529b 100644 --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -4,10 +4,10 @@ import unittest from email import message_from_string, message_from_bytes from email.generator import Generator, BytesGenerator from email import policy -from test.test_email import TestEmailBase +from test.test_email import TestEmailBase, Parameterized -class TestGeneratorBase: +class TestGeneratorBase(metaclass=Parameterized): policy = policy.default @@ -80,31 +80,23 @@ class TestGeneratorBase: "\n" "None\n") - def _test_maxheaderlen_parameter(self, n): + length_params = [n for n in refold_long_expected] + + def length_as_maxheaderlen_parameter(self, n): msg = self.msgmaker(self.typ(self.refold_long_expected[0])) s = self.ioclass() g = self.genclass(s, maxheaderlen=n, policy=self.policy) g.flatten(msg) self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n])) - for n in refold_long_expected: - locals()['test_maxheaderlen_parameter_' + str(n)] = ( - lambda self, n=n: - self._test_maxheaderlen_parameter(n)) - - def _test_max_line_length_policy(self, n): + def length_as_max_line_length_policy(self, n): msg = self.msgmaker(self.typ(self.refold_long_expected[0])) s = self.ioclass() g = self.genclass(s, policy=self.policy.clone(max_line_length=n)) g.flatten(msg) self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n])) - for n in refold_long_expected: - locals()['test_max_line_length_policy' + str(n)] = ( - lambda self, n=n: - self._test_max_line_length_policy(n)) - - def _test_maxheaderlen_parm_overrides_policy(self, n): + def length_as_maxheaderlen_parm_overrides_policy(self, n): msg = self.msgmaker(self.typ(self.refold_long_expected[0])) s = self.ioclass() g = self.genclass(s, maxheaderlen=n, @@ -112,12 +104,7 @@ class TestGeneratorBase: g.flatten(msg) self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n])) - for n in refold_long_expected: - locals()['test_maxheaderlen_parm_overrides_policy' + str(n)] = ( - lambda self, n=n: - self._test_maxheaderlen_parm_overrides_policy(n)) - - def _test_refold_none_does_not_fold(self, n): + def length_as_max_line_length_with_refold_none_does_not_fold(self, n): msg = self.msgmaker(self.typ(self.refold_long_expected[0])) s = self.ioclass() g = self.genclass(s, policy=self.policy.clone(refold_source='none', @@ -125,12 +112,7 @@ class TestGeneratorBase: g.flatten(msg) self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[0])) - for n in refold_long_expected: - locals()['test_refold_none_does_not_fold' + str(n)] = ( - lambda self, n=n: - self._test_refold_none_does_not_fold(n)) - - def _test_refold_all(self, n): + def length_as_max_line_length_with_refold_all_folds(self, n): msg = self.msgmaker(self.typ(self.refold_long_expected[0])) s = self.ioclass() g = self.genclass(s, policy=self.policy.clone(refold_source='all', @@ -138,11 +120,6 @@ class TestGeneratorBase: g.flatten(msg) self.assertEqual(s.getvalue(), self.typ(self.refold_all_expected[n])) - for n in refold_long_expected: - locals()['test_refold_all' + str(n)] = ( - lambda self, n=n: - self._test_refold_all(n)) - def test_crlf_control_via_policy(self): source = "Subject: test\r\n\r\ntest body\r\n" expected = source diff --git a/Lib/test/test_email/test_headerregistry.py b/Lib/test/test_email/test_headerregistry.py index e6fe29b..4a57ff1 100644 --- a/Lib/test/test_email/test_headerregistry.py +++ b/Lib/test/test_email/test_headerregistry.py @@ -4,7 +4,7 @@ import unittest from email import errors from email import policy from email.message import Message -from test.test_email import TestEmailBase +from test.test_email import TestEmailBase, Parameterized from email import headerregistry from email.headerregistry import Address, Group @@ -175,9 +175,9 @@ class TestDateHeader(TestHeaderBase): self.assertEqual(m['Date'].datetime, self.dt) -class TestAddressHeader(TestHeaderBase): +class TestAddressHeader(TestHeaderBase, metaclass=Parameterized): - examples = { + example_params = { 'empty': ('<>', @@ -305,8 +305,8 @@ class TestAddressHeader(TestHeaderBase): # trailing comments, which aren't currently handled. comments in # general are not handled yet. - def _test_single_addr(self, source, defects, decoded, display_name, - addr_spec, username, domain, comment): + def example_as_address(self, source, defects, decoded, display_name, + addr_spec, username, domain, comment): h = self.make_header('sender', source) self.assertEqual(h, decoded) self.assertDefectsEqual(h.defects, defects) @@ -322,13 +322,8 @@ class TestAddressHeader(TestHeaderBase): # XXX: we have no comment support yet. #self.assertEqual(a.comment, comment) - for name in examples: - locals()['test_'+name] = ( - lambda self, name=name: - self._test_single_addr(*self.examples[name])) - - def _test_group_single_addr(self, source, defects, decoded, display_name, - addr_spec, username, domain, comment): + def example_as_group(self, source, defects, decoded, display_name, + addr_spec, username, domain, comment): source = 'foo: {};'.format(source) gdecoded = 'foo: {};'.format(decoded) if decoded else 'foo:;' h = self.make_header('to', source) @@ -344,11 +339,6 @@ class TestAddressHeader(TestHeaderBase): self.assertEqual(a.username, username) self.assertEqual(a.domain, domain) - for name in examples: - locals()['test_group_'+name] = ( - lambda self, name=name: - self._test_group_single_addr(*self.examples[name])) - def test_simple_address_list(self): value = ('Fred <dinsdale@python.org>, foo@example.com, ' '"Harry W. Hastings" <hasty@example.com>') @@ -366,7 +356,7 @@ class TestAddressHeader(TestHeaderBase): 'Harry W. Hastings') def test_complex_address_list(self): - examples = list(self.examples.values()) + examples = list(self.example_params.values()) source = ('dummy list:;, another: (empty);,' + ', '.join([x[0] for x in examples[:4]]) + ', ' + r'"A \"list\"": ' + diff --git a/Lib/test/test_email/test_pickleable.py b/Lib/test/test_email/test_pickleable.py index 3a5bd11..09477e0 100644 --- a/Lib/test/test_email/test_pickleable.py +++ b/Lib/test/test_email/test_pickleable.py @@ -6,83 +6,66 @@ import email import email.message from email import policy from email.headerregistry import HeaderRegistry -from test.test_email import TestEmailBase +from test.test_email import TestEmailBase, Parameterized -class TestPickleCopyHeader(TestEmailBase): +class TestPickleCopyHeader(TestEmailBase, metaclass=Parameterized): header_factory = HeaderRegistry() unstructured = header_factory('subject', 'this is a test') - def _test_deepcopy(self, name, value): + header_params = { + 'subject': ('subject', 'this is a test'), + 'from': ('from', 'frodo@mordor.net'), + 'to': ('to', 'a: k@b.com, y@z.com;, j@f.com'), + 'date': ('date', 'Tue, 29 May 2012 09:24:26 +1000'), + } + + def header_as_deepcopy(self, name, value): header = self.header_factory(name, value) h = copy.deepcopy(header) self.assertEqual(str(h), str(header)) - def _test_pickle(self, name, value): + def header_as_pickle(self, name, value): header = self.header_factory(name, value) p = pickle.dumps(header) h = pickle.loads(p) self.assertEqual(str(h), str(header)) - headers = ( - ('subject', 'this is a test'), - ('from', 'frodo@mordor.net'), - ('to', 'a: k@b.com, y@z.com;, j@f.com'), - ('date', 'Tue, 29 May 2012 09:24:26 +1000'), - ) - - for header in headers: - locals()['test_deepcopy_'+header[0]] = ( - lambda self, header=header: - self._test_deepcopy(*header)) - - for header in headers: - locals()['test_pickle_'+header[0]] = ( - lambda self, header=header: - self._test_pickle(*header)) +class TestPickleCopyMessage(TestEmailBase, metaclass=Parameterized): -class TestPickleCopyMessage(TestEmailBase): - - msgs = {} + # Message objects are a sequence, so we have to make them a one-tuple in + # msg_params so they get passed to the parameterized test method as a + # single argument instead of as a list of headers. + msg_params = {} # Note: there will be no custom header objects in the parsed message. - msgs['parsed'] = email.message_from_string(textwrap.dedent("""\ + msg_params['parsed'] = (email.message_from_string(textwrap.dedent("""\ Date: Tue, 29 May 2012 09:24:26 +1000 From: frodo@mordor.net To: bilbo@underhill.org Subject: help I think I forgot the ring. - """), policy=policy.default) + """), policy=policy.default),) - msgs['created'] = email.message.Message(policy=policy.default) - msgs['created']['Date'] = 'Tue, 29 May 2012 09:24:26 +1000' - msgs['created']['From'] = 'frodo@mordor.net' - msgs['created']['To'] = 'bilbo@underhill.org' - msgs['created']['Subject'] = 'help' - msgs['created'].set_payload('I think I forgot the ring.') + msg_params['created'] = (email.message.Message(policy=policy.default),) + msg_params['created'][0]['Date'] = 'Tue, 29 May 2012 09:24:26 +1000' + msg_params['created'][0]['From'] = 'frodo@mordor.net' + msg_params['created'][0]['To'] = 'bilbo@underhill.org' + msg_params['created'][0]['Subject'] = 'help' + msg_params['created'][0].set_payload('I think I forgot the ring.') - def _test_deepcopy(self, msg): + def msg_as_deepcopy(self, msg): msg2 = copy.deepcopy(msg) self.assertEqual(msg2.as_string(), msg.as_string()) - def _test_pickle(self, msg): + def msg_as_pickle(self, msg): p = pickle.dumps(msg) msg2 = pickle.loads(p) self.assertEqual(msg2.as_string(), msg.as_string()) - for name, msg in msgs.items(): - locals()['test_deepcopy_'+name] = ( - lambda self, msg=msg: - self._test_deepcopy(msg)) - - for name, msg in msgs.items(): - locals()['test_pickle_'+name] = ( - lambda self, msg=msg: - self._test_pickle(msg)) - if __name__ == '__main__': unittest.main() |