comparison python-daemon-2.0.5/test_version.py @ 33:7ceb967147c3

start xena with no gui add library files
author jingchunzhu <jingchunzhu@gmail.com>
date Wed, 22 Jul 2015 13:24:44 -0700
parents
children
comparison
equal deleted inserted replaced
32:63b1ba1e3424 33:7ceb967147c3
1 # -*- coding: utf-8 -*-
2 #
3 # test_version.py
4 # Part of ‘python-daemon’, an implementation of PEP 3143.
5 #
6 # Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
7 #
8 # This is free software: you may copy, modify, and/or distribute this work
9 # under the terms of the GNU General Public License as published by the
10 # Free Software Foundation; version 3 of that license or any later version.
11 # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
12
13 """ Unit test for ‘version’ packaging module. """
14
15 from __future__ import (absolute_import, unicode_literals)
16
17 import os
18 import os.path
19 import io
20 import errno
21 import functools
22 import collections
23 import textwrap
24 import json
25 import tempfile
26 import distutils.dist
27 import distutils.cmd
28 import distutils.errors
29 import distutils.fancy_getopt
30 try:
31 # Standard library of Python 2.7 and later.
32 from io import StringIO
33 except ImportError:
34 # Standard library of Python 2.6 and earlier.
35 from StringIO import StringIO
36
37 import mock
38 import testtools
39 import testscenarios
40 import docutils
41 import docutils.writers
42 import docutils.nodes
43 import setuptools
44 import setuptools.command
45
46 import version
47
48 version.ensure_class_bases_begin_with(
49 version.__dict__, str('VersionInfoWriter'), docutils.writers.Writer)
50 version.ensure_class_bases_begin_with(
51 version.__dict__, str('VersionInfoTranslator'),
52 docutils.nodes.SparseNodeVisitor)
53
54
55 def make_test_classes_for_ensure_class_bases_begin_with():
56 """ Make test classes for use with ‘ensure_class_bases_begin_with’.
57
58 :return: Mapping {`name`: `type`} of the custom types created.
59
60 """
61
62 class quux_metaclass(type):
63 def __new__(metaclass, name, bases, namespace):
64 return super(quux_metaclass, metaclass).__new__(
65 metaclass, name, bases, namespace)
66
67 class Foo(object):
68 __metaclass__ = type
69
70 class Bar(object):
71 pass
72
73 class FooInheritingBar(Bar):
74 __metaclass__ = type
75
76 class FooWithCustomMetaclass(object):
77 __metaclass__ = quux_metaclass
78
79 result = dict(
80 (name, value) for (name, value) in locals().items()
81 if isinstance(value, type))
82
83 return result
84
85 class ensure_class_bases_begin_with_TestCase(
86 testscenarios.WithScenarios, testtools.TestCase):
87 """ Test cases for ‘ensure_class_bases_begin_with’ function. """
88
89 test_classes = make_test_classes_for_ensure_class_bases_begin_with()
90
91 scenarios = [
92 ('simple', {
93 'test_class': test_classes['Foo'],
94 'base_class': test_classes['Bar'],
95 }),
96 ('custom metaclass', {
97 'test_class': test_classes['FooWithCustomMetaclass'],
98 'base_class': test_classes['Bar'],
99 'expected_metaclass': test_classes['quux_metaclass'],
100 }),
101 ]
102
103 def setUp(self):
104 """ Set up test fixtures. """
105 super(ensure_class_bases_begin_with_TestCase, self).setUp()
106
107 self.class_name = self.test_class.__name__
108 self.test_module_namespace = {self.class_name: self.test_class}
109
110 if not hasattr(self, 'expected_metaclass'):
111 self.expected_metaclass = type
112
113 patcher_metaclass = mock.patch.object(
114 self.test_class, '__metaclass__')
115 patcher_metaclass.start()
116 self.addCleanup(patcher_metaclass.stop)
117
118 self.fake_new_class = type(object)
119 self.test_class.__metaclass__.return_value = (
120 self.fake_new_class)
121
122 def test_module_namespace_contains_new_class(self):
123 """ Specified module namespace should have new class. """
124 version.ensure_class_bases_begin_with(
125 self.test_module_namespace, self.class_name, self.base_class)
126 self.assertIn(self.fake_new_class, self.test_module_namespace.values())
127
128 def test_calls_metaclass_with_expected_class_name(self):
129 """ Should call the metaclass with the expected class name. """
130 version.ensure_class_bases_begin_with(
131 self.test_module_namespace, self.class_name, self.base_class)
132 expected_class_name = self.class_name
133 self.test_class.__metaclass__.assert_called_with(
134 expected_class_name, mock.ANY, mock.ANY)
135
136 def test_calls_metaclass_with_expected_bases(self):
137 """ Should call the metaclass with the expected bases. """
138 version.ensure_class_bases_begin_with(
139 self.test_module_namespace, self.class_name, self.base_class)
140 expected_bases = tuple(
141 [self.base_class]
142 + list(self.test_class.__bases__))
143 self.test_class.__metaclass__.assert_called_with(
144 mock.ANY, expected_bases, mock.ANY)
145
146 def test_calls_metaclass_with_expected_namespace(self):
147 """ Should call the metaclass with the expected class namespace. """
148 version.ensure_class_bases_begin_with(
149 self.test_module_namespace, self.class_name, self.base_class)
150 expected_namespace = self.test_class.__dict__.copy()
151 del expected_namespace['__dict__']
152 self.test_class.__metaclass__.assert_called_with(
153 mock.ANY, mock.ANY, expected_namespace)
154
155
156 class ensure_class_bases_begin_with_AlreadyHasBase_TestCase(
157 testscenarios.WithScenarios, testtools.TestCase):
158 """ Test cases for ‘ensure_class_bases_begin_with’ function.
159
160 These test cases test the conditions where the class's base is
161 already the specified base class.
162
163 """
164
165 test_classes = make_test_classes_for_ensure_class_bases_begin_with()
166
167 scenarios = [
168 ('already Bar subclass', {
169 'test_class': test_classes['FooInheritingBar'],
170 'base_class': test_classes['Bar'],
171 }),
172 ]
173
174 def setUp(self):
175 """ Set up test fixtures. """
176 super(
177 ensure_class_bases_begin_with_AlreadyHasBase_TestCase,
178 self).setUp()
179
180 self.class_name = self.test_class.__name__
181 self.test_module_namespace = {self.class_name: self.test_class}
182
183 patcher_metaclass = mock.patch.object(
184 self.test_class, '__metaclass__')
185 patcher_metaclass.start()
186 self.addCleanup(patcher_metaclass.stop)
187
188 def test_metaclass_not_called(self):
189 """ Should not call metaclass to create a new type. """
190 version.ensure_class_bases_begin_with(
191 self.test_module_namespace, self.class_name, self.base_class)
192 self.assertFalse(self.test_class.__metaclass__.called)
193
194
195 class VersionInfoWriter_TestCase(testtools.TestCase):
196 """ Test cases for ‘VersionInfoWriter’ class. """
197
198 def setUp(self):
199 """ Set up test fixtures. """
200 super(VersionInfoWriter_TestCase, self).setUp()
201
202 self.test_instance = version.VersionInfoWriter()
203
204 def test_declares_version_info_support(self):
205 """ Should declare support for ‘version_info’. """
206 instance = self.test_instance
207 expected_support = "version_info"
208 result = instance.supports(expected_support)
209 self.assertTrue(result)
210
211
212 class VersionInfoWriter_translate_TestCase(testtools.TestCase):
213 """ Test cases for ‘VersionInfoWriter.translate’ method. """
214
215 def setUp(self):
216 """ Set up test fixtures. """
217 super(VersionInfoWriter_translate_TestCase, self).setUp()
218
219 patcher_translator = mock.patch.object(
220 version, 'VersionInfoTranslator')
221 self.mock_class_translator = patcher_translator.start()
222 self.addCleanup(patcher_translator.stop)
223 self.mock_translator = self.mock_class_translator.return_value
224
225 self.test_instance = version.VersionInfoWriter()
226 patcher_document = mock.patch.object(
227 self.test_instance, 'document')
228 patcher_document.start()
229 self.addCleanup(patcher_document.stop)
230
231 def test_creates_translator_with_document(self):
232 """ Should create a translator with the writer's document. """
233 instance = self.test_instance
234 expected_document = self.test_instance.document
235 instance.translate()
236 self.mock_class_translator.assert_called_with(expected_document)
237
238 def test_calls_document_walkabout_with_translator(self):
239 """ Should call document.walkabout with the translator. """
240 instance = self.test_instance
241 instance.translate()
242 instance.document.walkabout.assert_called_with(self.mock_translator)
243
244 def test_output_from_translator_astext(self):
245 """ Should have output from translator.astext(). """
246 instance = self.test_instance
247 instance.translate()
248 expected_output = self.mock_translator.astext.return_value
249 self.assertEqual(expected_output, instance.output)
250
251
252 class ChangeLogEntry_TestCase(testtools.TestCase):
253 """ Test cases for ‘ChangeLogEntry’ class. """
254
255 def setUp(self):
256 """ Set up test fixtures. """
257 super(ChangeLogEntry_TestCase, self).setUp()
258
259 self.test_instance = version.ChangeLogEntry()
260
261 def test_instantiate(self):
262 """ New instance of ‘ChangeLogEntry’ should be created. """
263 self.assertIsInstance(
264 self.test_instance, version.ChangeLogEntry)
265
266 def test_minimum_zero_arguments(self):
267 """ Initialiser should not require any arguments. """
268 instance = version.ChangeLogEntry()
269 self.assertIsNot(instance, None)
270
271
272 class ChangeLogEntry_release_date_TestCase(
273 testscenarios.WithScenarios, testtools.TestCase):
274 """ Test cases for ‘ChangeLogEntry.release_date’ attribute. """
275
276 scenarios = [
277 ('default', {
278 'test_args': {},
279 'expected_release_date':
280 version.ChangeLogEntry.default_release_date,
281 }),
282 ('unknown token', {
283 'test_args': {'release_date': "UNKNOWN"},
284 'expected_release_date': "UNKNOWN",
285 }),
286 ('future token', {
287 'test_args': {'release_date': "FUTURE"},
288 'expected_release_date': "FUTURE",
289 }),
290 ('2001-01-01', {
291 'test_args': {'release_date': "2001-01-01"},
292 'expected_release_date': "2001-01-01",
293 }),
294 ('bogus', {
295 'test_args': {'release_date': "b0gUs"},
296 'expected_error': ValueError,
297 }),
298 ]
299
300 def test_has_expected_release_date(self):
301 """ Should have default `release_date` attribute. """
302 if hasattr(self, 'expected_error'):
303 self.assertRaises(
304 self.expected_error,
305 version.ChangeLogEntry, **self.test_args)
306 else:
307 instance = version.ChangeLogEntry(**self.test_args)
308 self.assertEqual(self.expected_release_date, instance.release_date)
309
310
311 class ChangeLogEntry_version_TestCase(
312 testscenarios.WithScenarios, testtools.TestCase):
313 """ Test cases for ‘ChangeLogEntry.version’ attribute. """
314
315 scenarios = [
316 ('default', {
317 'test_args': {},
318 'expected_version':
319 version.ChangeLogEntry.default_version,
320 }),
321 ('unknown token', {
322 'test_args': {'version': "UNKNOWN"},
323 'expected_version': "UNKNOWN",
324 }),
325 ('0.0', {
326 'test_args': {'version': "0.0"},
327 'expected_version': "0.0",
328 }),
329 ]
330
331 def test_has_expected_version(self):
332 """ Should have default `version` attribute. """
333 instance = version.ChangeLogEntry(**self.test_args)
334 self.assertEqual(self.expected_version, instance.version)
335
336
337 class ChangeLogEntry_maintainer_TestCase(
338 testscenarios.WithScenarios, testtools.TestCase):
339 """ Test cases for ‘ChangeLogEntry.maintainer’ attribute. """
340
341 scenarios = [
342 ('default', {
343 'test_args': {},
344 'expected_maintainer': None,
345 }),
346 ('person', {
347 'test_args': {'maintainer': "Foo Bar <foo.bar@example.org>"},
348 'expected_maintainer': "Foo Bar <foo.bar@example.org>",
349 }),
350 ('bogus', {
351 'test_args': {'maintainer': "b0gUs"},
352 'expected_error': ValueError,
353 }),
354 ]
355
356 def test_has_expected_maintainer(self):
357 """ Should have default `maintainer` attribute. """
358 if hasattr(self, 'expected_error'):
359 self.assertRaises(
360 self.expected_error,
361 version.ChangeLogEntry, **self.test_args)
362 else:
363 instance = version.ChangeLogEntry(**self.test_args)
364 self.assertEqual(self.expected_maintainer, instance.maintainer)
365
366
367 class ChangeLogEntry_body_TestCase(
368 testscenarios.WithScenarios, testtools.TestCase):
369 """ Test cases for ‘ChangeLogEntry.body’ attribute. """
370
371 scenarios = [
372 ('default', {
373 'test_args': {},
374 'expected_body': None,
375 }),
376 ('simple', {
377 'test_args': {'body': "Foo bar baz."},
378 'expected_body': "Foo bar baz.",
379 }),
380 ]
381
382 def test_has_expected_body(self):
383 """ Should have default `body` attribute. """
384 instance = version.ChangeLogEntry(**self.test_args)
385 self.assertEqual(self.expected_body, instance.body)
386
387
388 class ChangeLogEntry_as_version_info_entry_TestCase(
389 testscenarios.WithScenarios, testtools.TestCase):
390 """ Test cases for ‘ChangeLogEntry.as_version_info_entry’ attribute. """
391
392 scenarios = [
393 ('default', {
394 'test_args': {},
395 'expected_result': collections.OrderedDict([
396 ('release_date', version.ChangeLogEntry.default_release_date),
397 ('version', version.ChangeLogEntry.default_version),
398 ('maintainer', None),
399 ('body', None),
400 ]),
401 }),
402 ]
403
404 def setUp(self):
405 """ Set up test fixtures. """
406 super(ChangeLogEntry_as_version_info_entry_TestCase, self).setUp()
407
408 self.test_instance = version.ChangeLogEntry(**self.test_args)
409
410 def test_returns_result(self):
411 """ Should return expected result. """
412 result = self.test_instance.as_version_info_entry()
413 self.assertEqual(self.expected_result, result)
414
415
416 def make_mock_field_node(field_name, field_body):
417 """ Make a mock Docutils field node for tests. """
418
419 mock_field_node = mock.MagicMock(
420 name='field', spec=docutils.nodes.field)
421
422 mock_field_name_node = mock.MagicMock(
423 name='field_name', spec=docutils.nodes.field_name)
424 mock_field_name_node.parent = mock_field_node
425 mock_field_name_node.children = [field_name]
426
427 mock_field_body_node = mock.MagicMock(
428 name='field_body', spec=docutils.nodes.field_body)
429 mock_field_body_node.parent = mock_field_node
430 mock_field_body_node.children = [field_body]
431
432 mock_field_node.children = [mock_field_name_node, mock_field_body_node]
433
434 def fake_func_first_child_matching_class(node_class):
435 result = None
436 node_class_name = node_class.__name__
437 for (index, node) in enumerate(mock_field_node.children):
438 if node._mock_name == node_class_name:
439 result = index
440 break
441 return result
442
443 mock_field_node.first_child_matching_class.side_effect = (
444 fake_func_first_child_matching_class)
445
446 return mock_field_node
447
448
449 class JsonEqual(testtools.matchers.Matcher):
450 """ A matcher to compare the value of JSON streams. """
451
452 def __init__(self, expected):
453 self.expected_value = expected
454
455 def match(self, content):
456 """ Assert the JSON `content` matches the `expected_content`. """
457 result = None
458 actual_value = json.loads(content.decode('utf-8'))
459 if actual_value != self.expected_value:
460 result = JsonValueMismatch(self.expected_value, actual_value)
461 return result
462
463
464 class JsonValueMismatch(testtools.matchers.Mismatch):
465 """ The specified JSON stream does not evaluate to the expected value. """
466
467 def __init__(self, expected, actual):
468 self.expected_value = expected
469 self.actual_value = actual
470
471 def describe(self):
472 """ Emit a text description of this mismatch. """
473 expected_json_text = json.dumps(self.expected_value, indent=4)
474 actual_json_text = json.dumps(self.actual_value, indent=4)
475 text = (
476 "\n"
477 "reference: {expected}\n"
478 "actual: {actual}\n").format(
479 expected=expected_json_text, actual=actual_json_text)
480 return text
481
482
483 class changelog_to_version_info_collection_TestCase(
484 testscenarios.WithScenarios, testtools.TestCase):
485 """ Test cases for ‘changelog_to_version_info_collection’ function. """
486
487 scenarios = [
488 ('single entry', {
489 'test_input': textwrap.dedent("""\
490 Version 1.0
491 ===========
492
493 :Released: 2009-01-01
494 :Maintainer: Foo Bar <foo.bar@example.org>
495
496 * Lorem ipsum dolor sit amet.
497 """),
498 'expected_version_info': [
499 {
500 'release_date': "2009-01-01",
501 'version': "1.0",
502 'maintainer': "Foo Bar <foo.bar@example.org>",
503 'body': "* Lorem ipsum dolor sit amet.\n",
504 },
505 ],
506 }),
507 ('multiple entries', {
508 'test_input': textwrap.dedent("""\
509 Version 1.0
510 ===========
511
512 :Released: 2009-01-01
513 :Maintainer: Foo Bar <foo.bar@example.org>
514
515 * Lorem ipsum dolor sit amet.
516
517
518 Version 0.8
519 ===========
520
521 :Released: 2004-01-01
522 :Maintainer: Foo Bar <foo.bar@example.org>
523
524 * Donec venenatis nisl aliquam ipsum.
525
526
527 Version 0.7.2
528 =============
529
530 :Released: 2001-01-01
531 :Maintainer: Foo Bar <foo.bar@example.org>
532
533 * Pellentesque elementum mollis finibus.
534 """),
535 'expected_version_info': [
536 {
537 'release_date': "2009-01-01",
538 'version': "1.0",
539 'maintainer': "Foo Bar <foo.bar@example.org>",
540 'body': "* Lorem ipsum dolor sit amet.\n",
541 },
542 {
543 'release_date': "2004-01-01",
544 'version': "0.8",
545 'maintainer': "Foo Bar <foo.bar@example.org>",
546 'body': "* Donec venenatis nisl aliquam ipsum.\n",
547 },
548 {
549 'release_date': "2001-01-01",
550 'version': "0.7.2",
551 'maintainer': "Foo Bar <foo.bar@example.org>",
552 'body': "* Pellentesque elementum mollis finibus.\n",
553 },
554 ],
555 }),
556 ('trailing comment', {
557 'test_input': textwrap.dedent("""\
558 Version NEXT
559 ============
560
561 :Released: FUTURE
562 :Maintainer:
563
564 * Lorem ipsum dolor sit amet.
565
566 ..
567 Vivamus aliquam felis rutrum rutrum dictum.
568 """),
569 'expected_version_info': [
570 {
571 'release_date': "FUTURE",
572 'version': "NEXT",
573 'maintainer': "",
574 'body': "* Lorem ipsum dolor sit amet.\n",
575 },
576 ],
577 }),
578 ('inline comment', {
579 'test_input': textwrap.dedent("""\
580 Version NEXT
581 ============
582
583 :Released: FUTURE
584 :Maintainer:
585
586 ..
587 Vivamus aliquam felis rutrum rutrum dictum.
588
589 * Lorem ipsum dolor sit amet.
590 """),
591 'expected_version_info': [
592 {
593 'release_date': "FUTURE",
594 'version': "NEXT",
595 'maintainer': "",
596 'body': "* Lorem ipsum dolor sit amet.\n",
597 },
598 ],
599 }),
600 ('unreleased entry', {
601 'test_input': textwrap.dedent("""\
602 Version NEXT
603 ============
604
605 :Released: FUTURE
606 :Maintainer:
607
608 * Lorem ipsum dolor sit amet.
609
610
611 Version 0.8
612 ===========
613
614 :Released: 2001-01-01
615 :Maintainer: Foo Bar <foo.bar@example.org>
616
617 * Donec venenatis nisl aliquam ipsum.
618 """),
619 'expected_version_info': [
620 {
621 'release_date': "FUTURE",
622 'version': "NEXT",
623 'maintainer': "",
624 'body': "* Lorem ipsum dolor sit amet.\n",
625 },
626 {
627 'release_date': "2001-01-01",
628 'version': "0.8",
629 'maintainer': "Foo Bar <foo.bar@example.org>",
630 'body': "* Donec venenatis nisl aliquam ipsum.\n",
631 },
632 ],
633 }),
634 ('no section', {
635 'test_input': textwrap.dedent("""\
636 :Released: 2009-01-01
637 :Maintainer: Foo Bar <foo.bar@example.org>
638
639 * Lorem ipsum dolor sit amet.
640 """),
641 'expected_error': version.InvalidFormatError,
642 }),
643 ('subsection', {
644 'test_input': textwrap.dedent("""\
645 Version 1.0
646 ===========
647
648 :Released: 2009-01-01
649 :Maintainer: Foo Bar <foo.bar@example.org>
650
651 * Lorem ipsum dolor sit amet.
652
653 Ut ultricies fermentum quam
654 ---------------------------
655
656 * In commodo magna facilisis in.
657 """),
658 'expected_error': version.InvalidFormatError,
659 'subsection': True,
660 }),
661 ('unknown field', {
662 'test_input': textwrap.dedent("""\
663 Version 1.0
664 ===========
665
666 :Released: 2009-01-01
667 :Maintainer: Foo Bar <foo.bar@example.org>
668 :Favourite: Spam
669
670 * Lorem ipsum dolor sit amet.
671 """),
672 'expected_error': version.InvalidFormatError,
673 }),
674 ('invalid version word', {
675 'test_input': textwrap.dedent("""\
676 BoGuS 1.0
677 =========
678
679 :Released: 2009-01-01
680 :Maintainer: Foo Bar <foo.bar@example.org>
681
682 * Lorem ipsum dolor sit amet.
683 """),
684 'expected_error': version.InvalidFormatError,
685 }),
686 ('invalid section title', {
687 'test_input': textwrap.dedent("""\
688 Lorem Ipsum 1.0
689 ===============
690
691 :Released: 2009-01-01
692 :Maintainer: Foo Bar <foo.bar@example.org>
693
694 * Lorem ipsum dolor sit amet.
695 """),
696 'expected_error': version.InvalidFormatError,
697 }),
698 ]
699
700 def test_returns_expected_version_info(self):
701 """ Should return expected version info mapping. """
702 infile = StringIO(self.test_input)
703 if hasattr(self, 'expected_error'):
704 self.assertRaises(
705 self.expected_error,
706 version.changelog_to_version_info_collection, infile)
707 else:
708 result = version.changelog_to_version_info_collection(infile)
709 self.assertThat(result, JsonEqual(self.expected_version_info))
710
711
712 try:
713 FileNotFoundError
714 PermissionError
715 except NameError:
716 # Python 2 uses OSError.
717 FileNotFoundError = functools.partial(IOError, errno.ENOENT)
718 PermissionError = functools.partial(IOError, errno.EPERM)
719
720 fake_version_info = {
721 'release_date': "2001-01-01", 'version': "2.0",
722 'maintainer': None, 'body': None,
723 }
724
725 @mock.patch.object(
726 version, "get_latest_version", return_value=fake_version_info)
727 class generate_version_info_from_changelog_TestCase(
728 testscenarios.WithScenarios, testtools.TestCase):
729 """ Test cases for ‘generate_version_info_from_changelog’ function. """
730
731 fake_open_side_effects = {
732 'success': (
733 lambda *args, **kwargs: StringIO()),
734 'file not found': FileNotFoundError(),
735 'permission denied': PermissionError(),
736 }
737
738 scenarios = [
739 ('simple', {
740 'open_scenario': 'success',
741 'fake_versions_json': json.dumps([fake_version_info]),
742 'expected_result': fake_version_info,
743 }),
744 ('file not found', {
745 'open_scenario': 'file not found',
746 'expected_result': {},
747 }),
748 ('permission denied', {
749 'open_scenario': 'permission denied',
750 'expected_result': {},
751 }),
752 ]
753
754 def setUp(self):
755 """ Set up test fixtures. """
756 super(generate_version_info_from_changelog_TestCase, self).setUp()
757
758 self.fake_changelog_file_path = tempfile.mktemp()
759
760 def fake_open(filespec, *args, **kwargs):
761 if filespec == self.fake_changelog_file_path:
762 side_effect = self.fake_open_side_effects[self.open_scenario]
763 if callable(side_effect):
764 result = side_effect()
765 else:
766 raise side_effect
767 else:
768 result = StringIO()
769 return result
770
771 func_patcher_io_open = mock.patch.object(
772 io, "open")
773 func_patcher_io_open.start()
774 self.addCleanup(func_patcher_io_open.stop)
775 io.open.side_effect = fake_open
776
777 self.file_encoding = "utf-8"
778
779 func_patcher_changelog_to_version_info_collection = mock.patch.object(
780 version, "changelog_to_version_info_collection")
781 func_patcher_changelog_to_version_info_collection.start()
782 self.addCleanup(func_patcher_changelog_to_version_info_collection.stop)
783 if hasattr(self, 'fake_versions_json'):
784 version.changelog_to_version_info_collection.return_value = (
785 self.fake_versions_json.encode(self.file_encoding))
786
787 def test_returns_empty_collection_on_read_error(
788 self,
789 mock_func_get_latest_version):
790 """ Should return empty collection on error reading changelog. """
791 test_error = PermissionError("Not for you")
792 version.changelog_to_version_info_collection.side_effect = test_error
793 result = version.generate_version_info_from_changelog(
794 self.fake_changelog_file_path)
795 expected_result = {}
796 self.assertDictEqual(expected_result, result)
797
798 def test_opens_file_with_expected_encoding(
799 self,
800 mock_func_get_latest_version):
801 """ Should open changelog file in text mode with expected encoding. """
802 result = version.generate_version_info_from_changelog(
803 self.fake_changelog_file_path)
804 expected_file_path = self.fake_changelog_file_path
805 expected_open_mode = 'rt'
806 expected_encoding = self.file_encoding
807 (open_args_positional, open_args_kwargs) = io.open.call_args
808 (open_args_filespec, open_args_mode) = open_args_positional[:2]
809 open_args_encoding = open_args_kwargs['encoding']
810 self.assertEqual(expected_file_path, open_args_filespec)
811 self.assertEqual(expected_open_mode, open_args_mode)
812 self.assertEqual(expected_encoding, open_args_encoding)
813
814 def test_returns_expected_result(
815 self,
816 mock_func_get_latest_version):
817 """ Should return expected result. """
818 result = version.generate_version_info_from_changelog(
819 self.fake_changelog_file_path)
820 self.assertEqual(self.expected_result, result)
821
822
823 DefaultNoneDict = functools.partial(collections.defaultdict, lambda: None)
824
825 class get_latest_version_TestCase(
826 testscenarios.WithScenarios, testtools.TestCase):
827 """ Test cases for ‘get_latest_version’ function. """
828
829 scenarios = [
830 ('simple', {
831 'test_versions': [
832 DefaultNoneDict({'release_date': "LATEST"}),
833 ],
834 'expected_result': version.ChangeLogEntry.make_ordered_dict(
835 DefaultNoneDict({'release_date': "LATEST"})),
836 }),
837 ('no versions', {
838 'test_versions': [],
839 'expected_result': collections.OrderedDict(),
840 }),
841 ('ordered versions', {
842 'test_versions': [
843 DefaultNoneDict({'release_date': "1"}),
844 DefaultNoneDict({'release_date': "2"}),
845 DefaultNoneDict({'release_date': "LATEST"}),
846 ],
847 'expected_result': version.ChangeLogEntry.make_ordered_dict(
848 DefaultNoneDict({'release_date': "LATEST"})),
849 }),
850 ('un-ordered versions', {
851 'test_versions': [
852 DefaultNoneDict({'release_date': "2"}),
853 DefaultNoneDict({'release_date': "LATEST"}),
854 DefaultNoneDict({'release_date': "1"}),
855 ],
856 'expected_result': version.ChangeLogEntry.make_ordered_dict(
857 DefaultNoneDict({'release_date': "LATEST"})),
858 }),
859 ]
860
861 def test_returns_expected_result(self):
862 """ Should return expected result. """
863 result = version.get_latest_version(self.test_versions)
864 self.assertDictEqual(self.expected_result, result)
865
866
867 @mock.patch.object(json, "dumps", side_effect=json.dumps)
868 class serialise_version_info_from_mapping_TestCase(
869 testscenarios.WithScenarios, testtools.TestCase):
870 """ Test cases for ‘get_latest_version’ function. """
871
872 scenarios = [
873 ('simple', {
874 'test_version_info': {'foo': "spam"},
875 }),
876 ]
877
878 for (name, scenario) in scenarios:
879 scenario['fake_json_dump'] = json.dumps(scenario['test_version_info'])
880 scenario['expected_value'] = scenario['test_version_info']
881
882 def test_passes_specified_object(self, mock_func_json_dumps):
883 """ Should pass the specified object to `json.dumps`. """
884 result = version.serialise_version_info_from_mapping(
885 self.test_version_info)
886 mock_func_json_dumps.assert_called_with(
887 self.test_version_info, indent=mock.ANY)
888
889 def test_returns_expected_result(self, mock_func_json_dumps):
890 """ Should return expected result. """
891 mock_func_json_dumps.return_value = self.fake_json_dump
892 result = version.serialise_version_info_from_mapping(
893 self.test_version_info)
894 value = json.loads(result)
895 self.assertEqual(self.expected_value, value)
896
897
898 DistributionMetadata_defaults = {
899 name: None
900 for name in list(collections.OrderedDict.fromkeys(
901 distutils.dist.DistributionMetadata._METHOD_BASENAMES))}
902 FakeDistributionMetadata = collections.namedtuple(
903 'FakeDistributionMetadata', DistributionMetadata_defaults.keys())
904
905 Distribution_defaults = {
906 'metadata': None,
907 'version': None,
908 'release_date': None,
909 'maintainer': None,
910 'maintainer_email': None,
911 }
912 FakeDistribution = collections.namedtuple(
913 'FakeDistribution', Distribution_defaults.keys())
914
915 def make_fake_distribution(
916 fields_override=None, metadata_fields_override=None):
917 metadata_fields = DistributionMetadata_defaults.copy()
918 if metadata_fields_override is not None:
919 metadata_fields.update(metadata_fields_override)
920 metadata = FakeDistributionMetadata(**metadata_fields)
921
922 fields = Distribution_defaults.copy()
923 fields['metadata'] = metadata
924 if fields_override is not None:
925 fields.update(fields_override)
926 distribution = FakeDistribution(**fields)
927
928 return distribution
929
930
931 class get_changelog_path_TestCase(
932 testscenarios.WithScenarios, testtools.TestCase):
933 """ Test cases for ‘get_changelog_path’ function. """
934
935 default_path = "."
936 default_script_filename = "setup.py"
937
938 scenarios = [
939 ('simple', {}),
940 ('unusual script name', {
941 'script_filename': "lorem_ipsum",
942 }),
943 ('relative script path', {
944 'script_directory': "dolor/sit/amet",
945 }),
946 ('absolute script path', {
947 'script_directory': "/dolor/sit/amet",
948 }),
949 ('specify filename', {
950 'changelog_filename': "adipiscing",
951 }),
952 ]
953
954 def setUp(self):
955 """ Set up test fixtures. """
956 super(get_changelog_path_TestCase, self).setUp()
957
958 self.test_distribution = mock.MagicMock(distutils.dist.Distribution)
959
960 if not hasattr(self, 'script_directory'):
961 self.script_directory = self.default_path
962 if not hasattr(self, 'script_filename'):
963 self.script_filename = self.default_script_filename
964 self.test_distribution.script_name = os.path.join(
965 self.script_directory, self.script_filename)
966
967 changelog_filename = version.changelog_filename
968 if hasattr(self, 'changelog_filename'):
969 changelog_filename = self.changelog_filename
970
971 self.expected_result = os.path.join(
972 self.script_directory, changelog_filename)
973
974 def test_returns_expected_result(self):
975 """ Should return expected result. """
976 args = {
977 'distribution': self.test_distribution,
978 }
979 if hasattr(self, 'changelog_filename'):
980 args.update({'filename': self.changelog_filename})
981 result = version.get_changelog_path(**args)
982 self.assertEqual(self.expected_result, result)
983
984
985 class WriteVersionInfoCommand_BaseTestCase(
986 testscenarios.WithScenarios, testtools.TestCase):
987 """ Base class for ‘WriteVersionInfoCommand’ test case classes. """
988
989 def setUp(self):
990 """ Set up test fixtures. """
991 super(WriteVersionInfoCommand_BaseTestCase, self).setUp()
992
993 fake_distribution_name = self.getUniqueString()
994
995 self.test_distribution = distutils.dist.Distribution()
996 self.test_distribution.metadata.name = fake_distribution_name
997
998
999 class WriteVersionInfoCommand_TestCase(WriteVersionInfoCommand_BaseTestCase):
1000 """ Test cases for ‘WriteVersionInfoCommand’ class. """
1001
1002 def test_subclass_of_distutils_command(self):
1003 """ Should be a subclass of ‘distutils.cmd.Command’. """
1004 instance = version.WriteVersionInfoCommand(self.test_distribution)
1005 self.assertIsInstance(instance, distutils.cmd.Command)
1006
1007
1008 class WriteVersionInfoCommand_user_options_TestCase(
1009 WriteVersionInfoCommand_BaseTestCase):
1010 """ Test cases for ‘WriteVersionInfoCommand.user_options’ attribute. """
1011
1012 def setUp(self):
1013 """ Set up test fixtures. """
1014 super(WriteVersionInfoCommand_user_options_TestCase, self).setUp()
1015
1016 self.test_instance = version.WriteVersionInfoCommand(
1017 self.test_distribution)
1018 self.commandline_parser = distutils.fancy_getopt.FancyGetopt(
1019 self.test_instance.user_options)
1020
1021 def test_parses_correctly_as_fancy_getopt(self):
1022 """ Should parse correctly in ‘FancyGetopt’. """
1023 self.assertIsInstance(
1024 self.commandline_parser, distutils.fancy_getopt.FancyGetopt)
1025
1026 def test_includes_base_class_user_options(self):
1027 """ Should include base class's user_options. """
1028 base_command = setuptools.command.egg_info.egg_info
1029 expected_user_options = base_command.user_options
1030 self.assertThat(
1031 set(expected_user_options),
1032 IsSubset(set(self.test_instance.user_options)))
1033
1034 def test_has_option_changelog_path(self):
1035 """ Should have a ‘changelog-path’ option. """
1036 expected_option_name = "changelog-path="
1037 result = self.commandline_parser.has_option(expected_option_name)
1038 self.assertTrue(result)
1039
1040 def test_has_option_outfile_path(self):
1041 """ Should have a ‘outfile-path’ option. """
1042 expected_option_name = "outfile-path="
1043 result = self.commandline_parser.has_option(expected_option_name)
1044 self.assertTrue(result)
1045
1046
1047 class WriteVersionInfoCommand_initialize_options_TestCase(
1048 WriteVersionInfoCommand_BaseTestCase):
1049 """ Test cases for ‘WriteVersionInfoCommand.initialize_options’ method. """
1050
1051 def setUp(self):
1052 """ Set up test fixtures. """
1053 super(
1054 WriteVersionInfoCommand_initialize_options_TestCase, self
1055 ).setUp()
1056
1057 patcher_func_egg_info_initialize_options = mock.patch.object(
1058 setuptools.command.egg_info.egg_info, "initialize_options")
1059 patcher_func_egg_info_initialize_options.start()
1060 self.addCleanup(patcher_func_egg_info_initialize_options.stop)
1061
1062 def test_calls_base_class_method(self):
1063 """ Should call base class's ‘initialize_options’ method. """
1064 instance = version.WriteVersionInfoCommand(self.test_distribution)
1065 base_command_class = setuptools.command.egg_info.egg_info
1066 base_command_class.initialize_options.assert_called_with()
1067
1068 def test_sets_changelog_path_to_none(self):
1069 """ Should set ‘changelog_path’ attribute to ``None``. """
1070 instance = version.WriteVersionInfoCommand(self.test_distribution)
1071 self.assertIs(instance.changelog_path, None)
1072
1073 def test_sets_outfile_path_to_none(self):
1074 """ Should set ‘outfile_path’ attribute to ``None``. """
1075 instance = version.WriteVersionInfoCommand(self.test_distribution)
1076 self.assertIs(instance.outfile_path, None)
1077
1078
1079 class WriteVersionInfoCommand_finalize_options_TestCase(
1080 WriteVersionInfoCommand_BaseTestCase):
1081 """ Test cases for ‘WriteVersionInfoCommand.finalize_options’ method. """
1082
1083 def setUp(self):
1084 """ Set up test fixtures. """
1085 super(WriteVersionInfoCommand_finalize_options_TestCase, self).setUp()
1086
1087 self.test_instance = version.WriteVersionInfoCommand(self.test_distribution)
1088
1089 patcher_func_egg_info_finalize_options = mock.patch.object(
1090 setuptools.command.egg_info.egg_info, "finalize_options")
1091 patcher_func_egg_info_finalize_options.start()
1092 self.addCleanup(patcher_func_egg_info_finalize_options.stop)
1093
1094 self.fake_script_dir = self.getUniqueString()
1095 self.test_distribution.script_name = os.path.join(
1096 self.fake_script_dir, self.getUniqueString())
1097
1098 self.fake_egg_dir = self.getUniqueString()
1099 self.test_instance.egg_info = self.fake_egg_dir
1100
1101 patcher_func_get_changelog_path = mock.patch.object(
1102 version, "get_changelog_path")
1103 patcher_func_get_changelog_path.start()
1104 self.addCleanup(patcher_func_get_changelog_path.stop)
1105
1106 self.fake_changelog_path = self.getUniqueString()
1107 version.get_changelog_path.return_value = self.fake_changelog_path
1108
1109 def test_calls_base_class_method(self):
1110 """ Should call base class's ‘finalize_options’ method. """
1111 base_command_class = setuptools.command.egg_info.egg_info
1112 self.test_instance.finalize_options()
1113 base_command_class.finalize_options.assert_called_with()
1114
1115 def test_sets_force_to_none(self):
1116 """ Should set ‘force’ attribute to ``None``. """
1117 self.test_instance.finalize_options()
1118 self.assertIs(self.test_instance.force, None)
1119
1120 def test_sets_changelog_path_using_get_changelog_path(self):
1121 """ Should set ‘changelog_path’ attribute if it was ``None``. """
1122 self.test_instance.changelog_path = None
1123 self.test_instance.finalize_options()
1124 expected_changelog_path = self.fake_changelog_path
1125 self.assertEqual(expected_changelog_path, self.test_instance.changelog_path)
1126
1127 def test_leaves_changelog_path_if_already_set(self):
1128 """ Should leave ‘changelog_path’ attribute set. """
1129 prior_changelog_path = self.getUniqueString()
1130 self.test_instance.changelog_path = prior_changelog_path
1131 self.test_instance.finalize_options()
1132 expected_changelog_path = prior_changelog_path
1133 self.assertEqual(expected_changelog_path, self.test_instance.changelog_path)
1134
1135 def test_sets_outfile_path_to_default(self):
1136 """ Should set ‘outfile_path’ attribute to default value. """
1137 fake_version_info_filename = self.getUniqueString()
1138 with mock.patch.object(
1139 version, "version_info_filename",
1140 new=fake_version_info_filename):
1141 self.test_instance.finalize_options()
1142 expected_outfile_path = os.path.join(
1143 self.fake_egg_dir, fake_version_info_filename)
1144 self.assertEqual(expected_outfile_path, self.test_instance.outfile_path)
1145
1146 def test_leaves_outfile_path_if_already_set(self):
1147 """ Should leave ‘outfile_path’ attribute set. """
1148 prior_outfile_path = self.getUniqueString()
1149 self.test_instance.outfile_path = prior_outfile_path
1150 self.test_instance.finalize_options()
1151 expected_outfile_path = prior_outfile_path
1152 self.assertEqual(expected_outfile_path, self.test_instance.outfile_path)
1153
1154
1155 class has_changelog_TestCase(
1156 testscenarios.WithScenarios, testtools.TestCase):
1157 """ Test cases for ‘has_changelog’ function. """
1158
1159 fake_os_path_exists_side_effects = {
1160 'true': (lambda path: True),
1161 'false': (lambda path: False),
1162 }
1163
1164 scenarios = [
1165 ('no changelog path', {
1166 'changelog_path': None,
1167 'expected_result': False,
1168 }),
1169 ('changelog exists', {
1170 'os_path_exists_scenario': 'true',
1171 'expected_result': True,
1172 }),
1173 ('changelog not found', {
1174 'os_path_exists_scenario': 'false',
1175 'expected_result': False,
1176 }),
1177 ]
1178
1179 def setUp(self):
1180 """ Set up test fixtures. """
1181 super(has_changelog_TestCase, self).setUp()
1182
1183 self.test_distribution = distutils.dist.Distribution()
1184 self.test_command = version.EggInfoCommand(
1185 self.test_distribution)
1186
1187 patcher_func_get_changelog_path = mock.patch.object(
1188 version, "get_changelog_path")
1189 patcher_func_get_changelog_path.start()
1190 self.addCleanup(patcher_func_get_changelog_path.stop)
1191
1192 self.fake_changelog_file_path = self.getUniqueString()
1193 if hasattr(self, 'changelog_path'):
1194 self.fake_changelog_file_path = self.changelog_path
1195 version.get_changelog_path.return_value = self.fake_changelog_file_path
1196 self.fake_changelog_file = StringIO()
1197
1198 def fake_os_path_exists(path):
1199 if path == self.fake_changelog_file_path:
1200 side_effect = self.fake_os_path_exists_side_effects[
1201 self.os_path_exists_scenario]
1202 if callable(side_effect):
1203 result = side_effect(path)
1204 else:
1205 raise side_effect
1206 else:
1207 result = False
1208 return result
1209
1210 func_patcher_os_path_exists = mock.patch.object(
1211 os.path, "exists")
1212 func_patcher_os_path_exists.start()
1213 self.addCleanup(func_patcher_os_path_exists.stop)
1214 os.path.exists.side_effect = fake_os_path_exists
1215
1216 def test_gets_changelog_path_from_distribution(self):
1217 """ Should call ‘get_changelog_path’ with distribution. """
1218 result = version.has_changelog(self.test_command)
1219 version.get_changelog_path.assert_called_with(
1220 self.test_distribution)
1221
1222 def test_returns_expected_result(self):
1223 """ Should be a subclass of ‘distutils.cmd.Command’. """
1224 result = version.has_changelog(self.test_command)
1225 self.assertEqual(self.expected_result, result)
1226
1227
1228 @mock.patch.object(version, 'generate_version_info_from_changelog')
1229 @mock.patch.object(version, 'serialise_version_info_from_mapping')
1230 @mock.patch.object(version.EggInfoCommand, "write_file")
1231 class WriteVersionInfoCommand_run_TestCase(
1232 WriteVersionInfoCommand_BaseTestCase):
1233 """ Test cases for ‘WriteVersionInfoCommand.run’ method. """
1234
1235 def setUp(self):
1236 """ Set up test fixtures. """
1237 super(WriteVersionInfoCommand_run_TestCase, self).setUp()
1238
1239 self.test_instance = version.WriteVersionInfoCommand(
1240 self.test_distribution)
1241
1242 self.fake_changelog_path = self.getUniqueString()
1243 self.test_instance.changelog_path = self.fake_changelog_path
1244
1245 self.fake_outfile_path = self.getUniqueString()
1246 self.test_instance.outfile_path = self.fake_outfile_path
1247
1248 def test_returns_none(
1249 self,
1250 mock_func_egg_info_write_file,
1251 mock_func_serialise_version_info,
1252 mock_func_generate_version_info):
1253 """ Should return ``None``. """
1254 result = self.test_instance.run()
1255 self.assertIs(result, None)
1256
1257 def test_generates_version_info_from_changelog(
1258 self,
1259 mock_func_egg_info_write_file,
1260 mock_func_serialise_version_info,
1261 mock_func_generate_version_info):
1262 """ Should generate version info from specified changelog. """
1263 self.test_instance.run()
1264 expected_changelog_path = self.test_instance.changelog_path
1265 mock_func_generate_version_info.assert_called_with(
1266 expected_changelog_path)
1267
1268 def test_serialises_version_info_from_mapping(
1269 self,
1270 mock_func_egg_info_write_file,
1271 mock_func_serialise_version_info,
1272 mock_func_generate_version_info):
1273 """ Should serialise version info from specified mapping. """
1274 self.test_instance.run()
1275 expected_version_info = mock_func_generate_version_info.return_value
1276 mock_func_serialise_version_info.assert_called_with(
1277 expected_version_info)
1278
1279 def test_writes_file_using_command_context(
1280 self,
1281 mock_func_egg_info_write_file,
1282 mock_func_serialise_version_info,
1283 mock_func_generate_version_info):
1284 """ Should write the metadata file using the command context. """
1285 self.test_instance.run()
1286 expected_content = mock_func_serialise_version_info.return_value
1287 mock_func_egg_info_write_file.assert_called_with(
1288 "version info", self.fake_outfile_path, expected_content)
1289
1290
1291 IsSubset = testtools.matchers.MatchesPredicateWithParams(
1292 set.issubset, "{0} should be a subset of {1}")
1293
1294 class EggInfoCommand_TestCase(testtools.TestCase):
1295 """ Test cases for ‘EggInfoCommand’ class. """
1296
1297 def setUp(self):
1298 """ Set up test fixtures. """
1299 super(EggInfoCommand_TestCase, self).setUp()
1300
1301 self.test_distribution = distutils.dist.Distribution()
1302 self.test_instance = version.EggInfoCommand(self.test_distribution)
1303
1304 def test_subclass_of_setuptools_egg_info(self):
1305 """ Should be a subclass of Setuptools ‘egg_info’. """
1306 self.assertIsInstance(
1307 self.test_instance, setuptools.command.egg_info.egg_info)
1308
1309 def test_sub_commands_include_base_class_sub_commands(self):
1310 """ Should include base class's sub-commands in this sub_commands. """
1311 base_command = setuptools.command.egg_info.egg_info
1312 expected_sub_commands = base_command.sub_commands
1313 self.assertThat(
1314 set(expected_sub_commands),
1315 IsSubset(set(self.test_instance.sub_commands)))
1316
1317 def test_sub_commands_includes_write_version_info_command(self):
1318 """ Should include sub-command named ‘write_version_info’. """
1319 commands_by_name = dict(self.test_instance.sub_commands)
1320 expected_predicate = version.has_changelog
1321 expected_item = ('write_version_info', expected_predicate)
1322 self.assertIn(expected_item, commands_by_name.items())
1323
1324
1325 @mock.patch.object(setuptools.command.egg_info.egg_info, "run")
1326 class EggInfoCommand_run_TestCase(testtools.TestCase):
1327 """ Test cases for ‘EggInfoCommand.run’ method. """
1328
1329 def setUp(self):
1330 """ Set up test fixtures. """
1331 super(EggInfoCommand_run_TestCase, self).setUp()
1332
1333 self.test_distribution = distutils.dist.Distribution()
1334 self.test_instance = version.EggInfoCommand(self.test_distribution)
1335
1336 base_command = setuptools.command.egg_info.egg_info
1337 patcher_func_egg_info_get_sub_commands = mock.patch.object(
1338 base_command, "get_sub_commands")
1339 patcher_func_egg_info_get_sub_commands.start()
1340 self.addCleanup(patcher_func_egg_info_get_sub_commands.stop)
1341
1342 patcher_func_egg_info_run_command = mock.patch.object(
1343 base_command, "run_command")
1344 patcher_func_egg_info_run_command.start()
1345 self.addCleanup(patcher_func_egg_info_run_command.stop)
1346
1347 self.fake_sub_commands = ["spam", "eggs", "beans"]
1348 base_command.get_sub_commands.return_value = self.fake_sub_commands
1349
1350 def test_returns_none(self, mock_func_egg_info_run):
1351 """ Should return ``None``. """
1352 result = self.test_instance.run()
1353 self.assertIs(result, None)
1354
1355 def test_runs_each_command_in_sub_commands(
1356 self, mock_func_egg_info_run):
1357 """ Should run each command in ‘self.get_sub_commands()’. """
1358 base_command = setuptools.command.egg_info.egg_info
1359 self.test_instance.run()
1360 expected_calls = [mock.call(name) for name in self.fake_sub_commands]
1361 base_command.run_command.assert_has_calls(expected_calls)
1362
1363 def test_calls_base_class_run(self, mock_func_egg_info_run):
1364 """ Should call base class's ‘run’ method. """
1365 result = self.test_instance.run()
1366 mock_func_egg_info_run.assert_called_with()
1367
1368
1369 # Local variables:
1370 # coding: utf-8
1371 # mode: python
1372 # End:
1373 # vim: fileencoding=utf-8 filetype=python :