LEFT | RIGHT |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 ''' | 2 ''' |
3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's | 3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's |
4 Smalltalk testing framework. | 4 Smalltalk testing framework. |
5 | 5 |
6 This module contains the core framework classes that form the basis of | 6 This module contains the core framework classes that form the basis of |
7 specific test cases and suites (TestCase, TestSuite etc.), and also a | 7 specific test cases and suites (TestCase, TestSuite etc.), and also a |
8 text-based utility class for running the tests and reporting the results | 8 text-based utility class for running the tests and reporting the results |
9 (TextTestRunner). | 9 (TextTestRunner). |
10 | 10 |
11 Simple usage: | 11 Simple usage: |
12 | 12 |
13 import unittest | 13 import unittest |
14 | 14 |
15 class IntegerArithmenticTestCase(unittest.TestCase): | 15 class IntegerArithmenticTestCase(unittest.TestCase): |
16 def testAdd(self): ## test method names begin 'test*' | 16 def testAdd(self): ## test method names begin 'test*' |
17 self.assertEquals((1 + 2), 3) | 17 self.assertEqual((1 + 2), 3) |
18 self.assertEquals(0 + 1, 1) | 18 self.assertEqual(0 + 1, 1) |
19 def testMultiply(self): | 19 def testMultiply(self): |
20 self.assertEquals((0 * 10), 0) | 20 self.assertEqual((0 * 10), 0) |
21 self.assertEquals((5 * 8), 40) | 21 self.assertEqual((5 * 8), 40) |
22 | 22 |
23 if __name__ == '__main__': | 23 if __name__ == '__main__': |
24 unittest.main() | 24 unittest.main() |
25 | 25 |
26 Further information is available in the bundled documentation, and from | 26 Further information is available in the bundled documentation, and from |
27 | 27 |
28 http://docs.python.org/library/unittest.html | 28 http://docs.python.org/library/unittest.html |
29 | 29 |
30 Copyright (c) 1999-2003 Steve Purcell | 30 Copyright (c) 1999-2003 Steve Purcell |
31 Copyright (c) 2003-2009 Python Software Foundation | 31 Copyright (c) 2003-2009 Python Software Foundation |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
247 length += 1 | 247 length += 1 |
248 tb = tb.tb_next | 248 tb = tb.tb_next |
249 return length | 249 return length |
250 | 250 |
251 def __repr__(self): | 251 def __repr__(self): |
252 return "<%s run=%i errors=%i failures=%i>" % \ | 252 return "<%s run=%i errors=%i failures=%i>" % \ |
253 (_strclass(self.__class__), self.testsRun, len(self.errors), | 253 (_strclass(self.__class__), self.testsRun, len(self.errors), |
254 len(self.failures)) | 254 len(self.failures)) |
255 | 255 |
256 | 256 |
257 class AssertRaisesContext(object): | 257 class _AssertRaisesContext(object): |
| 258 """A context manager used to implement TestCase.assertRaises* methods.""" |
258 | 259 |
259 def __init__(self, expected, test_case, expected_regexp=None): | 260 def __init__(self, expected, test_case, expected_regexp=None): |
260 self.expected = expected | 261 self.expected = expected |
261 self.failureException = test_case.failureException | 262 self.failureException = test_case.failureException |
262 self.expected_regex = expected_regexp | 263 self.expected_regex = expected_regexp |
263 | 264 |
264 def __enter__(self): | 265 def __enter__(self): |
265 pass | 266 pass |
266 | 267 |
267 def __exit__(self, exc_type, exc_value, traceback): | 268 def __exit__(self, exc_type, exc_value, traceback): |
268 if exc_type is None: | 269 if exc_type is None: |
269 try: | 270 try: |
270 exc_name = self.expected.__name__ | 271 exc_name = self.expected.__name__ |
271 except AttributeError: | 272 except AttributeError: |
272 exc_name = str(self.expected) | 273 exc_name = str(self.expected) |
273 raise self.failureException( | 274 raise self.failureException( |
274 "{0} not raised".format(exc_name)) | 275 "{0} not raised".format(exc_name)) |
275 if not issubclass(exc_type, self.expected): | 276 if not issubclass(exc_type, self.expected): |
276 # let unexpexted exceptions pass through | 277 # let unexpexted exceptions pass through |
277 return False | 278 return False |
278 if self.expected_regex is None: | 279 if self.expected_regex is None: |
279 return True | 280 return True |
280 | 281 |
281 expected_regexp = self.expected_regex | 282 expected_regexp = self.expected_regex |
282 if isinstance(expected_regexp, basestring): | 283 if isinstance(expected_regexp, basestring): |
283 expected_regexp = re.compile(expected_regexp) | 284 expected_regexp = re.compile(expected_regexp) |
284 if not expected_regexp.search(str(exc_value)): | 285 if not expected_regexp.search(str(exc_value)): |
285 raise self.failureException('"%s" does not match "%s"' % | 286 raise self.failureException('"%s" does not match "%s"' % |
286 (expected_regexp.pattern, str(exc_value))) | 287 (expected_regexp.pattern, str(exc_value))) |
287 return True | 288 return True |
288 | 289 |
289 | 290 |
290 | 291 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 not have a method with the specified name. | 323 not have a method with the specified name. |
323 """ | 324 """ |
324 self._testMethodName = methodName | 325 self._testMethodName = methodName |
325 try: | 326 try: |
326 testMethod = getattr(self, methodName) | 327 testMethod = getattr(self, methodName) |
327 except AttributeError: | 328 except AttributeError: |
328 raise ValueError("no such test method in %s: %s" % \ | 329 raise ValueError("no such test method in %s: %s" % \ |
329 (self.__class__, methodName)) | 330 (self.__class__, methodName)) |
330 self._testMethodDoc = testMethod.__doc__ | 331 self._testMethodDoc = testMethod.__doc__ |
331 | 332 |
332 # Map types to custom assertEquals functions that will compare | 333 # Map types to custom assertEqual functions that will compare |
333 # instances of said type in more detail to generate a more useful | 334 # instances of said type in more detail to generate a more useful |
334 # error message. | 335 # error message. |
335 self.__type_equality_funcs = {} | 336 self.__type_equality_funcs = {} |
336 self.addTypeEqualityFunc(dict, self.assertDictEquals) | 337 self.addTypeEqualityFunc(dict, self.assertDictEqual) |
337 self.addTypeEqualityFunc(list, self.assertListEquals) | 338 self.addTypeEqualityFunc(list, self.assertListEqual) |
338 self.addTypeEqualityFunc(tuple, self.assertTupleEquals) | 339 self.addTypeEqualityFunc(tuple, self.assertTupleEqual) |
339 self.addTypeEqualityFunc(set, self.assertSetEquals) | 340 self.addTypeEqualityFunc(set, self.assertSetEqual) |
340 self.addTypeEqualityFunc(frozenset, self.assertSetEquals) | 341 self.addTypeEqualityFunc(frozenset, self.assertSetEqual) |
341 | 342 |
342 def addTypeEqualityFunc(self, typeobj, function): | 343 def addTypeEqualityFunc(self, typeobj, function): |
343 """Add a type specific assertEqual style function to compare a type. | 344 """Add a type specific assertEqual style function to compare a type. |
344 | 345 |
345 This method is for use by TestCase subclasses that need to register | 346 This method is for use by TestCase subclasses that need to register |
346 their own type equality functions to provide nicer error messages. | 347 their own type equality functions to provide nicer error messages. |
347 | 348 |
348 Args: | 349 Args: |
349 typeobj: The data type to call this function on when both values | 350 typeobj: The data type to call this function on when both values |
350 are of the same type in assertEquals(). | 351 are of the same type in assertEqual(). |
351 function: The callable taking two arguments and an optional | 352 function: The callable taking two arguments and an optional |
352 msg= argument that raises self.failureException with a | 353 msg= argument that raises self.failureException with a |
353 useful error message when the two arguments are not equal. | 354 useful error message when the two arguments are not equal. |
354 """ | 355 """ |
355 self.__type_equality_funcs[typeobj] = function | 356 self.__type_equality_funcs[typeobj] = function |
356 | 357 |
357 def setUp(self): | 358 def setUp(self): |
358 "Hook method for setting up the test fixture before exercising it." | 359 "Hook method for setting up the test fixture before exercising it." |
359 pass | 360 pass |
360 | 361 |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
483 thrown, it will not be caught, and the test case will be | 484 thrown, it will not be caught, and the test case will be |
484 deemed to have suffered an error, exactly as for an | 485 deemed to have suffered an error, exactly as for an |
485 unexpected exception. | 486 unexpected exception. |
486 | 487 |
487 If called with callableObj omitted or None, will return a | 488 If called with callableObj omitted or None, will return a |
488 context object used like this:: | 489 context object used like this:: |
489 | 490 |
490 with self.failUnlessRaises(some_error_class): | 491 with self.failUnlessRaises(some_error_class): |
491 do_something() | 492 do_something() |
492 """ | 493 """ |
493 context = AssertRaisesContext(excClass, self) | 494 context = _AssertRaisesContext(excClass, self) |
494 if callableObj is None: | 495 if callableObj is None: |
495 return context | 496 return context |
496 with context: | 497 with context: |
497 callableObj(*args, **kwargs) | 498 callableObj(*args, **kwargs) |
498 | 499 |
499 def _getAssertEqualityFunc(self, first, second): | 500 def _getAssertEqualityFunc(self, first, second): |
500 """Get a detailed comparison function for the types of the two args. | 501 """Get a detailed comparison function for the types of the two args. |
501 | 502 |
502 Returns: A callable accepting (first, second, msg=None) that will | 503 Returns: A callable accepting (first, second, msg=None) that will |
503 raise a failure exception if first != second with a useful human | 504 raise a failure exception if first != second with a useful human |
504 readable error message for those types. | 505 readable error message for those types. |
505 """ | 506 """ |
506 # | 507 # |
507 # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) | 508 # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) |
508 # and vice versa. I opted for the conservative approach in case | 509 # and vice versa. I opted for the conservative approach in case |
509 # subclasses are not intended to be compared in detail to their super | 510 # subclasses are not intended to be compared in detail to their super |
510 # class instances using a type equality func. This means testing | 511 # class instances using a type equality func. This means testing |
511 # subtypes won't automagically use the detailed comparison. Callers | 512 # subtypes won't automagically use the detailed comparison. Callers |
512 # should use their type specific assertSpamEquals method to compare | 513 # should use their type specific assertSpamEqual method to compare |
513 # subclasses if the detailed comparison is desired and appropriate. | 514 # subclasses if the detailed comparison is desired and appropriate. |
514 # See the discussion in http://bugs.python.org/issue2578. | 515 # See the discussion in http://bugs.python.org/issue2578. |
515 # | 516 # |
516 if type(first) is type(second): | 517 if type(first) is type(second): |
517 return self.__type_equality_funcs.get(type(first), | 518 return self.__type_equality_funcs.get(type(first), |
518 self._baseAssertEquals) | 519 self._baseAssertEqual) |
519 return self._baseAssertEquals | 520 return self._baseAssertEqual |
520 | 521 |
521 def _baseAssertEquals(self, first, second, msg=None): | 522 def _baseAssertEqual(self, first, second, msg=None): |
522 """The default assertEquals implementation, not type specific.""" | 523 """The default assertEqual implementation, not type specific.""" |
523 if not first == second: | 524 if not first == second: |
524 raise self.failureException(msg or '%r != %r' % (first, second)) | 525 raise self.failureException(msg or '%r != %r' % (first, second)) |
525 | 526 |
526 def failUnlessEqual(self, first, second, msg=None): | 527 def failUnlessEqual(self, first, second, msg=None): |
527 """Fail if the two objects are unequal as determined by the '==' | 528 """Fail if the two objects are unequal as determined by the '==' |
528 operator. | 529 operator. |
529 """ | 530 """ |
530 assertion_func = self._getAssertEqualityFunc(first, second) | 531 assertion_func = self._getAssertEqualityFunc(first, second) |
531 assertion_func(first, second, msg=msg) | 532 assertion_func(first, second, msg=msg) |
532 | 533 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
571 | 572 |
572 assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual | 573 assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual |
573 | 574 |
574 assertRaises = failUnlessRaises | 575 assertRaises = failUnlessRaises |
575 | 576 |
576 assert_ = assertTrue = failUnless | 577 assert_ = assertTrue = failUnless |
577 | 578 |
578 assertFalse = failIf | 579 assertFalse = failIf |
579 | 580 |
580 | 581 |
581 def assertSequenceEquals(self, seq1, seq2, msg=None, seq_type=None): | 582 def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): |
582 """An equality assertion for ordered sequences (like lists and tuples). | 583 """An equality assertion for ordered sequences (like lists and tuples). |
583 | 584 |
584 For the purposes of this function, a valid orderd sequence type is one | 585 For the purposes of this function, a valid orderd sequence type is one |
585 which can be indexed, has a length, and has an equality operator. | 586 which can be indexed, has a length, and has an equality operator. |
586 | 587 |
587 Args: | 588 Args: |
588 seq1: The first sequence to compare. | 589 seq1: The first sequence to compare. |
589 seq2: The second sequence to compare. | 590 seq2: The second sequence to compare. |
590 seq_type: The expected datatype of the sequences, or None if no | 591 seq_type: The expected datatype of the sequences, or None if no |
591 datatype should be enforced. | 592 datatype should be enforced. |
592 msg: Optional message to use on failure instead of a list of | 593 msg: Optional message to use on failure instead of a list of |
593 differences. | 594 differences. |
594 """ | 595 """ |
595 if seq_type != None: | 596 if seq_type != None: |
596 seq_type_name = seq_type.__name__ | 597 seq_type_name = seq_type.__name__ |
597 if not isinstance(seq1, seq_type): | 598 if not isinstance(seq1, seq_type): |
598 raise self.failureException('First sequence is not a %s: %r' | 599 raise self.failureException('First sequence is not a %s: %r' |
599 % (seq_type_name, seq1)) | 600 % (seq_type_name, seq1)) |
600 if not isinstance(seq2, seq_type): | 601 if not isinstance(seq2, seq_type): |
601 raise self.failureException('Second sequence is not a %s: %r' | 602 raise self.failureException('Second sequence is not a %s: %r' |
602 % (seq_type_name, seq2)) | 603 % (seq_type_name, seq2)) |
603 else: | 604 else: |
604 seq_type_name = "sequence" | 605 seq_type_name = "sequence" |
605 | 606 |
606 differing = None | 607 differing = None |
607 try: | 608 try: |
608 len1 = len(seq1) | 609 len1 = len(seq1) |
609 except (TypeError, NotImplementedError): | 610 except (TypeError, NotImplementedError): |
610 differing = 'First %s has no length. Non-sequence?' % ( | 611 differing = 'First %s has no length. Non-sequence?' % ( |
611 seq_type_name) | 612 seq_type_name) |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
664 differing += ('First extra element %d:\n%s\n' % | 665 differing += ('First extra element %d:\n%s\n' % |
665 (len1, seq2[len1])) | 666 (len1, seq2[len1])) |
666 except (TypeError, IndexError, NotImplementedError): | 667 except (TypeError, IndexError, NotImplementedError): |
667 differing += ('Unable to index element %d ' | 668 differing += ('Unable to index element %d ' |
668 'of second %s\n' % (len1, seq_type_name)) | 669 'of second %s\n' % (len1, seq_type_name)) |
669 if not msg: | 670 if not msg: |
670 msg = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), | 671 msg = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), |
671 pprint.pformat(seq2).splitlines())) | 672 pprint.pformat(seq2).splitlines())) |
672 self.fail(differing + msg) | 673 self.fail(differing + msg) |
673 | 674 |
674 def assertListEquals(self, list1, list2, msg=None): | 675 def assertListEqual(self, list1, list2, msg=None): |
675 """A list-specific equality assertion. | 676 """A list-specific equality assertion. |
676 | 677 |
677 Args: | 678 Args: |
678 list1: The first list to compare. | 679 list1: The first list to compare. |
679 list2: The second list to compare. | 680 list2: The second list to compare. |
680 msg: Optional message to use on failure instead of a list of | 681 msg: Optional message to use on failure instead of a list of |
681 differences. | 682 differences. |
682 | 683 |
683 """ | 684 """ |
684 self.assertSequenceEquals(list1, list2, msg, seq_type=list) | 685 self.assertSequenceEqual(list1, list2, msg, seq_type=list) |
685 | 686 |
686 def assertTupleEquals(self, tuple1, tuple2, msg=None): | 687 def assertTupleEqual(self, tuple1, tuple2, msg=None): |
687 """A tuple-specific equality assertion. | 688 """A tuple-specific equality assertion. |
688 | 689 |
689 Args: | 690 Args: |
690 tuple1: The first tuple to compare. | 691 tuple1: The first tuple to compare. |
691 tuple2: The second tuple to compare. | 692 tuple2: The second tuple to compare. |
692 msg: Optional message to use on failure instead of a list of | 693 msg: Optional message to use on failure instead of a list of |
693 differences. | 694 differences. |
694 """ | 695 """ |
695 self.assertSequenceEquals(tuple1, tuple2, msg, seq_type=tuple) | 696 self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple) |
696 | 697 |
697 def assertSetEquals(self, set1, set2, msg=None): | 698 def assertSetEqual(self, set1, set2, msg=None): |
698 """A set-specific equality assertion. | 699 """A set-specific equality assertion. |
699 | 700 |
700 Args: | 701 Args: |
701 set1: The first set to compare. | 702 set1: The first set to compare. |
702 set2: The second set to compare. | 703 set2: The second set to compare. |
703 msg: Optional message to use on failure instead of a list of | 704 msg: Optional message to use on failure instead of a list of |
704 differences. | 705 differences. |
705 | 706 |
706 For more general containership equality, assertSameElements will work | 707 For more general containership equality, assertSameElements will work |
707 with things other than sets. This uses ducktyping to support | 708 with things other than sets. This uses ducktyping to support |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
744 if msg is None: | 745 if msg is None: |
745 msg = '"%s" not found in "%s"' % (a, b) | 746 msg = '"%s" not found in "%s"' % (a, b) |
746 self.assert_(a in b, msg) | 747 self.assert_(a in b, msg) |
747 | 748 |
748 def assertNotIn(self, a, b, msg=None): | 749 def assertNotIn(self, a, b, msg=None): |
749 """Just like self.assert_(a not in b), but with a nicer default message.
""" | 750 """Just like self.assert_(a not in b), but with a nicer default message.
""" |
750 if msg is None: | 751 if msg is None: |
751 msg = '"%s" unexpectedly found in "%s"' % (a, b) | 752 msg = '"%s" unexpectedly found in "%s"' % (a, b) |
752 self.assert_(a not in b, msg) | 753 self.assert_(a not in b, msg) |
753 | 754 |
754 def assertDictEquals(self, d1, d2, msg=None): | 755 def assertDictEqual(self, d1, d2, msg=None): |
755 self.assert_(isinstance(d1, dict), 'First argument is not a dictionary') | 756 self.assert_(isinstance(d1, dict), 'First argument is not a dictionary') |
756 self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary'
) | 757 self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary'
) |
757 | 758 |
758 if d1 != d2: | 759 if d1 != d2: |
759 self.fail(msg or ('\n' + '\n'.join(difflib.ndiff( | 760 self.fail(msg or ('\n' + '\n'.join(difflib.ndiff( |
760 pprint.pformat(d1).splitlines(), | 761 pprint.pformat(d1).splitlines(), |
761 pprint.pformat(d2).splitlines())))) | 762 pprint.pformat(d2).splitlines())))) |
762 | 763 |
763 def assertDictContainsSubset(self, expected, actual, msg=None): | 764 def assertDictContainsSubset(self, expected, actual, msg=None): |
764 """Checks whether actual is a superset of expected.""" | 765 """Checks whether actual is a superset of expected.""" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
808 actual.sort() | 809 actual.sort() |
809 missing, unexpected = _SortedListDifference(expected, actual) | 810 missing, unexpected = _SortedListDifference(expected, actual) |
810 errors = [] | 811 errors = [] |
811 if missing: | 812 if missing: |
812 errors.append('Expected, but missing:\n %r\n' % missing) | 813 errors.append('Expected, but missing:\n %r\n' % missing) |
813 if unexpected: | 814 if unexpected: |
814 errors.append('Unexpected, but present:\n %r\n' % unexpected) | 815 errors.append('Unexpected, but present:\n %r\n' % unexpected) |
815 if errors: | 816 if errors: |
816 self.fail(msg or ''.join(errors)) | 817 self.fail(msg or ''.join(errors)) |
817 | 818 |
818 def assertMultiLineEquals(self, first, second, msg=None): | 819 def assertMultiLineEqual(self, first, second, msg=None): |
819 """Assert that two multi-line strings are equal.""" | 820 """Assert that two multi-line strings are equal.""" |
820 self.assert_(isinstance(first, types.StringTypes), ( | 821 self.assert_(isinstance(first, types.StringTypes), ( |
821 'First argument is not a string')) | 822 'First argument is not a string')) |
822 self.assert_(isinstance(second, types.StringTypes), ( | 823 self.assert_(isinstance(second, types.StringTypes), ( |
823 'Second argument is not a string')) | 824 'Second argument is not a string')) |
824 | 825 |
825 if first != second: | 826 if first != second: |
826 raise self.failureException( | 827 raise self.failureException( |
827 msg or '\n' + ''.join(difflib.ndiff(first.splitlines(True), | 828 msg or '\n' + ''.join(difflib.ndiff(first.splitlines(True), |
828
second.splitlines(True)))) | 829
second.splitlines(True)))) |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
866 """Asserts that the message in a raised exception matches a regexp. | 867 """Asserts that the message in a raised exception matches a regexp. |
867 | 868 |
868 Args: | 869 Args: |
869 expected_exception: Exception class expected to be raised. | 870 expected_exception: Exception class expected to be raised. |
870 expected_regexp: Regexp (re pattern object or string) expected | 871 expected_regexp: Regexp (re pattern object or string) expected |
871 to be found in error message. | 872 to be found in error message. |
872 callable_obj: Function to be called. | 873 callable_obj: Function to be called. |
873 args: Extra args. | 874 args: Extra args. |
874 kwargs: Extra kwargs. | 875 kwargs: Extra kwargs. |
875 """ | 876 """ |
876 context = AssertRaisesContext(expected_exception, self, expected_regexp) | 877 context = _AssertRaisesContext(expected_exception, self, expected_regexp
) |
877 if callable_obj is None: | 878 if callable_obj is None: |
878 return context | 879 return context |
879 with context: | 880 with context: |
880 callable_obj(*args, **kwargs) | 881 callable_obj(*args, **kwargs) |
881 | 882 |
882 def assertRegexpMatches(self, text, expected_regex, msg=None): | 883 def assertRegexpMatches(self, text, expected_regex, msg=None): |
883 if isinstance(expected_regex, basestring): | 884 if isinstance(expected_regex, basestring): |
884 expected_regex = re.compile(expected_regex) | 885 expected_regex = re.compile(expected_regex) |
885 if not expected_regex.search(text): | 886 if not expected_regex.search(text): |
886 msg = msg or "Regexp didn't match" | 887 msg = msg or "Regexp didn't match" |
(...skipping 579 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1466 | 1467 |
1467 main = TestProgram | 1468 main = TestProgram |
1468 | 1469 |
1469 | 1470 |
1470 ############################################################################## | 1471 ############################################################################## |
1471 # Executing this module from the command line | 1472 # Executing this module from the command line |
1472 ############################################################################## | 1473 ############################################################################## |
1473 | 1474 |
1474 if __name__ == "__main__": | 1475 if __name__ == "__main__": |
1475 main(module=None) | 1476 main(module=None) |
LEFT | RIGHT |