| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Unit tests for presubmit_support.py and presubmit_canned_checks.py.""" | 6 """Unit tests for presubmit_support.py and presubmit_canned_checks.py.""" |
| 7 | 7 |
| 8 # pylint is too confused. | 8 # pylint is too confused. |
| 9 # pylint: disable=E1101,E1103,W0212,W0403 | 9 # pylint: disable=E1101,E1103,W0212,W0403 |
| 10 | 10 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 return [output_api.PresubmitPromptWarning("??"), | 30 return [output_api.PresubmitPromptWarning("??"), |
| 31 output_api.PresubmitError("XX!!XX")] | 31 output_api.PresubmitError("XX!!XX")] |
| 32 else: | 32 else: |
| 33 return () | 33 return () |
| 34 """ | 34 """ |
| 35 presubmit_tryslave = """ | 35 presubmit_tryslave = """ |
| 36 def GetPreferredTrySlaves(): | 36 def GetPreferredTrySlaves(): |
| 37 return %s | 37 return %s |
| 38 """ | 38 """ |
| 39 | 39 |
| 40 presubmit_diffs = """ |
| 41 --- file1 2011-02-09 10:38:16.517224845 -0800 |
| 42 +++ file2 2011-02-09 10:38:53.177226516 -0800 |
| 43 @@ -1,6 +1,5 @@ |
| 44 this is line number 0 |
| 45 this is line number 1 |
| 46 -this is line number 2 to be deleted |
| 47 this is line number 3 |
| 48 this is line number 4 |
| 49 this is line number 5 |
| 50 @@ -8,7 +7,7 @@ |
| 51 this is line number 7 |
| 52 this is line number 8 |
| 53 this is line number 9 |
| 54 -this is line number 10 to be modified |
| 55 +this is line number 10 |
| 56 this is line number 11 |
| 57 this is line number 12 |
| 58 this is line number 13 |
| 59 @@ -21,9 +20,8 @@ |
| 60 this is line number 20 |
| 61 this is line number 21 |
| 62 this is line number 22 |
| 63 -this is line number 23 |
| 64 -this is line number 24 |
| 65 -this is line number 25 |
| 66 +this is line number 23.1 |
| 67 +this is line number 25.1 |
| 68 this is line number 26 |
| 69 this is line number 27 |
| 70 this is line number 28 |
| 71 @@ -31,6 +29,7 @@ |
| 72 this is line number 30 |
| 73 this is line number 31 |
| 74 this is line number 32 |
| 75 +this is line number 32.1 |
| 76 this is line number 33 |
| 77 this is line number 34 |
| 78 this is line number 35 |
| 79 @@ -38,14 +37,14 @@ |
| 80 this is line number 37 |
| 81 this is line number 38 |
| 82 this is line number 39 |
| 83 - |
| 84 this is line number 40 |
| 85 -this is line number 41 |
| 86 +this is line number 41.1 |
| 87 this is line number 42 |
| 88 this is line number 43 |
| 89 this is line number 44 |
| 90 this is line number 45 |
| 91 + |
| 92 this is line number 46 |
| 93 this is line number 47 |
| 94 -this is line number 48 |
| 95 +this is line number 48.1 |
| 96 this is line number 49 |
| 97 """ |
| 98 |
| 40 def setUp(self): | 99 def setUp(self): |
| 41 SuperMoxTestBase.setUp(self) | 100 SuperMoxTestBase.setUp(self) |
| 42 self.mox.StubOutWithMock(presubmit, 'random') | 101 self.mox.StubOutWithMock(presubmit, 'random') |
| 43 self.mox.StubOutWithMock(presubmit, 'warn') | 102 self.mox.StubOutWithMock(presubmit, 'warn') |
| 44 presubmit._ASKED_FOR_FEEDBACK = False | 103 presubmit._ASKED_FOR_FEEDBACK = False |
| 45 self.fake_root_dir = self.RootDir() | 104 self.fake_root_dir = self.RootDir() |
| 46 # Special mocks. | 105 # Special mocks. |
| 47 def MockAbsPath(f): | 106 def MockAbsPath(f): |
| 48 return f | 107 return f |
| 49 def MockChdir(f): | 108 def MockChdir(f): |
| 50 return None | 109 return None |
| 51 # SuperMoxTestBase already mock these but simplify our life. | 110 # SuperMoxTestBase already mock these but simplify our life. |
| 52 presubmit.os.path.abspath = MockAbsPath | 111 presubmit.os.path.abspath = MockAbsPath |
| 53 presubmit.os.getcwd = self.RootDir | 112 presubmit.os.getcwd = self.RootDir |
| 54 presubmit.os.chdir = MockChdir | 113 presubmit.os.chdir = MockChdir |
| 55 self.mox.StubOutWithMock(presubmit.scm.SVN, 'CaptureInfo') | 114 self.mox.StubOutWithMock(presubmit.scm.SVN, 'CaptureInfo') |
| 56 self.mox.StubOutWithMock(presubmit.scm.SVN, 'GetFileProperty') | 115 self.mox.StubOutWithMock(presubmit.scm.SVN, 'GetFileProperty') |
| 57 self.mox.StubOutWithMock(presubmit.gclient_utils, 'FileRead') | 116 self.mox.StubOutWithMock(presubmit.gclient_utils, 'FileRead') |
| 58 self.mox.StubOutWithMock(presubmit.gclient_utils, 'FileWrite') | 117 self.mox.StubOutWithMock(presubmit.gclient_utils, 'FileWrite') |
| 118 self.mox.StubOutWithMock(presubmit.scm.SVN, 'GenerateDiff') |
| 59 | 119 |
| 60 | 120 |
| 61 class PresubmitUnittest(PresubmitTestsBase): | 121 class PresubmitUnittest(PresubmitTestsBase): |
| 62 """General presubmit_support.py tests (excluding InputApi and OutputApi).""" | 122 """General presubmit_support.py tests (excluding InputApi and OutputApi).""" |
| 63 | 123 |
| 64 _INHERIT_SETTINGS = 'inherit-review-settings-ok' | 124 _INHERIT_SETTINGS = 'inherit-review-settings-ok' |
| 65 | 125 |
| 66 def testMembersChanged(self): | 126 def testMembersChanged(self): |
| 67 self.mox.ReplayAll() | 127 self.mox.ReplayAll() |
| 68 members = [ | 128 members = [ |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 binary, 'svn:mime-type').AndReturn('application/octet-stream') | 250 binary, 'svn:mime-type').AndReturn('application/octet-stream') |
| 191 presubmit.scm.SVN.GetFileProperty( | 251 presubmit.scm.SVN.GetFileProperty( |
| 192 notfound, 'svn:mime-type').AndReturn('') | 252 notfound, 'svn:mime-type').AndReturn('') |
| 193 presubmit.scm.SVN.CaptureInfo(blat).AndReturn( | 253 presubmit.scm.SVN.CaptureInfo(blat).AndReturn( |
| 194 {'URL': 'svn:/foo/foo/blat.cc'}) | 254 {'URL': 'svn:/foo/foo/blat.cc'}) |
| 195 presubmit.scm.SVN.CaptureInfo(binary).AndReturn( | 255 presubmit.scm.SVN.CaptureInfo(binary).AndReturn( |
| 196 {'URL': 'svn:/foo/binary.dll'}) | 256 {'URL': 'svn:/foo/binary.dll'}) |
| 197 presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({}) | 257 presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({}) |
| 198 presubmit.scm.SVN.CaptureInfo(flap).AndReturn( | 258 presubmit.scm.SVN.CaptureInfo(flap).AndReturn( |
| 199 {'URL': 'svn:/foo/boo/flap.h'}) | 259 {'URL': 'svn:/foo/boo/flap.h'}) |
| 200 presubmit.gclient_utils.FileRead(blat, 'rU').AndReturn('boo!\nahh?') | 260 presubmit.scm.SVN.GenerateDiff(blat).AndReturn(self.presubmit_diffs) |
| 201 presubmit.gclient_utils.FileRead(notfound, 'rU').AndReturn('look!\nthere?') | 261 presubmit.scm.SVN.GenerateDiff(notfound).AndReturn(self.presubmit_diffs) |
| 262 |
| 202 self.mox.ReplayAll() | 263 self.mox.ReplayAll() |
| 203 | 264 |
| 204 change = presubmit.SvnChange('mychange', '\n'.join(description_lines), | 265 change = presubmit.SvnChange('mychange', '\n'.join(description_lines), |
| 205 self.fake_root_dir, files, 0, 0) | 266 self.fake_root_dir, files, 0, 0) |
| 206 self.failUnless(change.Name() == 'mychange') | 267 self.failUnless(change.Name() == 'mychange') |
| 207 self.failUnless(change.DescriptionText() == | 268 self.failUnless(change.DescriptionText() == |
| 208 'Hello there\nthis is a change\nand some more regular text') | 269 'Hello there\nthis is a change\nand some more regular text') |
| 209 self.failUnless(change.FullDescriptionText() == | 270 self.failUnless(change.FullDescriptionText() == |
| 210 '\n'.join(description_lines)) | 271 '\n'.join(description_lines)) |
| 211 | 272 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 235 expected_paths.append('') # one unknown file | 296 expected_paths.append('') # one unknown file |
| 236 self.assertEqual( | 297 self.assertEqual( |
| 237 len(filter(lambda x: x in expected_paths, server_paths)), 4) | 298 len(filter(lambda x: x in expected_paths, server_paths)), 4) |
| 238 | 299 |
| 239 files = [[x[0], presubmit.normpath(x[1])] for x in files] | 300 files = [[x[0], presubmit.normpath(x[1])] for x in files] |
| 240 | 301 |
| 241 rhs_lines = [] | 302 rhs_lines = [] |
| 242 for line in change.RightHandSideLines(): | 303 for line in change.RightHandSideLines(): |
| 243 rhs_lines.append(line) | 304 rhs_lines.append(line) |
| 244 self.assertEquals(rhs_lines[0][0].LocalPath(), files[0][1]) | 305 self.assertEquals(rhs_lines[0][0].LocalPath(), files[0][1]) |
| 245 self.assertEquals(rhs_lines[0][1], 1) | 306 self.assertEquals(rhs_lines[0][1], 10) |
| 246 self.assertEquals(rhs_lines[0][2],'boo!') | 307 self.assertEquals(rhs_lines[0][2],'this is line number 10') |
| 247 | 308 |
| 248 self.assertEquals(rhs_lines[1][0].LocalPath(), files[0][1]) | 309 self.assertEquals(rhs_lines[3][0].LocalPath(), files[0][1]) |
| 249 self.assertEquals(rhs_lines[1][1], 2) | 310 self.assertEquals(rhs_lines[3][1], 32) |
| 250 self.assertEquals(rhs_lines[1][2], 'ahh?') | 311 self.assertEquals(rhs_lines[3][2], 'this is line number 32.1') |
| 251 | 312 |
| 252 self.assertEquals(rhs_lines[2][0].LocalPath(), files[3][1]) | 313 self.assertEquals(rhs_lines[8][0].LocalPath(), files[3][1]) |
| 253 self.assertEquals(rhs_lines[2][1], 1) | 314 self.assertEquals(rhs_lines[8][1], 23) |
| 254 self.assertEquals(rhs_lines[2][2], 'look!') | 315 self.assertEquals(rhs_lines[8][2], 'this is line number 23.1') |
| 255 | 316 |
| 256 self.assertEquals(rhs_lines[3][0].LocalPath(), files[3][1]) | 317 self.assertEquals(rhs_lines[12][0].LocalPath(), files[3][1]) |
| 257 self.assertEquals(rhs_lines[3][1], 2) | 318 self.assertEquals(rhs_lines[12][1], 46) |
| 258 self.assertEquals(rhs_lines[3][2], 'there?') | 319 self.assertEquals(rhs_lines[12][2], '') |
| 320 |
| 321 self.assertEquals(rhs_lines[13][0].LocalPath(), files[3][1]) |
| 322 self.assertEquals(rhs_lines[13][1], 49) |
| 323 self.assertEquals(rhs_lines[13][2], 'this is line number 48.1') |
| 259 | 324 |
| 260 def testExecPresubmitScript(self): | 325 def testExecPresubmitScript(self): |
| 261 description_lines = ('Hello there', | 326 description_lines = ('Hello there', |
| 262 'this is a change', | 327 'this is a change', |
| 263 'STORY=http://tracker/123') | 328 'STORY=http://tracker/123') |
| 264 files = [ | 329 files = [ |
| 265 ['A', 'foo\\blat.cc'], | 330 ['A', 'foo\\blat.cc'], |
| 266 ] | 331 ] |
| 267 fake_presubmit = presubmit.os.path.join(self.fake_root_dir, 'PRESUBMIT.py') | 332 fake_presubmit = presubmit.os.path.join(self.fake_root_dir, 'PRESUBMIT.py') |
| 268 self.mox.ReplayAll() | 333 self.mox.ReplayAll() |
| (...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 presubmit.scm.SVN.CaptureInfo(beingdeleted).AndReturn({}) | 769 presubmit.scm.SVN.CaptureInfo(beingdeleted).AndReturn({}) |
| 705 presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({}) | 770 presubmit.scm.SVN.CaptureInfo(notfound).AndReturn({}) |
| 706 presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None) | 771 presubmit.scm.SVN.GetFileProperty(blat, 'svn:mime-type').AndReturn(None) |
| 707 presubmit.scm.SVN.GetFileProperty(readme, 'svn:mime-type').AndReturn(None) | 772 presubmit.scm.SVN.GetFileProperty(readme, 'svn:mime-type').AndReturn(None) |
| 708 presubmit.scm.SVN.GetFileProperty(binary, 'svn:mime-type').AndReturn( | 773 presubmit.scm.SVN.GetFileProperty(binary, 'svn:mime-type').AndReturn( |
| 709 'application/octet-stream') | 774 'application/octet-stream') |
| 710 presubmit.scm.SVN.GetFileProperty(weird, 'svn:mime-type').AndReturn(None) | 775 presubmit.scm.SVN.GetFileProperty(weird, 'svn:mime-type').AndReturn(None) |
| 711 presubmit.scm.SVN.GetFileProperty(another, 'svn:mime-type').AndReturn(None) | 776 presubmit.scm.SVN.GetFileProperty(another, 'svn:mime-type').AndReturn(None) |
| 712 presubmit.scm.SVN.GetFileProperty(third_party, 'svn:mime-type' | 777 presubmit.scm.SVN.GetFileProperty(third_party, 'svn:mime-type' |
| 713 ).AndReturn(None) | 778 ).AndReturn(None) |
| 714 presubmit.gclient_utils.FileRead(blat, 'rU' | 779 presubmit.scm.SVN.GenerateDiff(blat).AndReturn(self.presubmit_diffs) |
| 715 ).AndReturn('whatever\ncookie') | 780 presubmit.scm.SVN.GenerateDiff(another).AndReturn(self.presubmit_diffs) |
| 716 presubmit.gclient_utils.FileRead(another, 'rU' | 781 |
| 717 ).AndReturn('whatever\ncookie2') | |
| 718 self.mox.ReplayAll() | 782 self.mox.ReplayAll() |
| 719 | 783 |
| 720 change = presubmit.SvnChange('mychange', '\n'.join(description_lines), | 784 change = presubmit.SvnChange('mychange', '\n'.join(description_lines), |
| 721 self.fake_root_dir, files, 0, 0) | 785 self.fake_root_dir, files, 0, 0) |
| 722 input_api = presubmit.InputApi(change, | 786 input_api = presubmit.InputApi(change, |
| 723 join(self.fake_root_dir, 'foo', | 787 join(self.fake_root_dir, 'foo', |
| 724 'PRESUBMIT.py'), | 788 'PRESUBMIT.py'), |
| 725 False) | 789 False) |
| 726 # Doesn't filter much | 790 # Doesn't filter much |
| 727 got_files = input_api.AffectedFiles() | 791 got_files = input_api.AffectedFiles() |
| 728 self.assertEquals(len(got_files), 7) | 792 self.assertEquals(len(got_files), 7) |
| 729 self.assertEquals(got_files[0].LocalPath(), presubmit.normpath(files[0][1])) | 793 self.assertEquals(got_files[0].LocalPath(), presubmit.normpath(files[0][1])) |
| 730 self.assertEquals(got_files[1].LocalPath(), presubmit.normpath(files[1][1])) | 794 self.assertEquals(got_files[1].LocalPath(), presubmit.normpath(files[1][1])) |
| 731 self.assertEquals(got_files[2].LocalPath(), presubmit.normpath(files[2][1])) | 795 self.assertEquals(got_files[2].LocalPath(), presubmit.normpath(files[2][1])) |
| 732 self.assertEquals(got_files[3].LocalPath(), presubmit.normpath(files[3][1])) | 796 self.assertEquals(got_files[3].LocalPath(), presubmit.normpath(files[3][1])) |
| 733 self.assertEquals(got_files[4].LocalPath(), presubmit.normpath(files[4][1])) | 797 self.assertEquals(got_files[4].LocalPath(), presubmit.normpath(files[4][1])) |
| 734 self.assertEquals(got_files[5].LocalPath(), presubmit.normpath(files[5][1])) | 798 self.assertEquals(got_files[5].LocalPath(), presubmit.normpath(files[5][1])) |
| 735 self.assertEquals(got_files[6].LocalPath(), presubmit.normpath(files[6][1])) | 799 self.assertEquals(got_files[6].LocalPath(), presubmit.normpath(files[6][1])) |
| 736 # Ignores weird because of whitelist, third_party because of blacklist, | 800 # Ignores weird because of whitelist, third_party because of blacklist, |
| 737 # binary isn't a text file and beingdeleted doesn't exist. The rest is | 801 # binary isn't a text file and beingdeleted doesn't exist. The rest is |
| 738 # outside foo/. | 802 # outside foo/. |
| 739 rhs_lines = [x for x in input_api.RightHandSideLines(None)] | 803 rhs_lines = [x for x in input_api.RightHandSideLines(None)] |
| 740 self.assertEquals(len(rhs_lines), 4) | 804 self.assertEquals(len(rhs_lines), 14) |
| 741 self.assertEqual(rhs_lines[0][0].LocalPath(), | 805 self.assertEqual(rhs_lines[0][0].LocalPath(), |
| 742 presubmit.normpath(files[0][1])) | 806 presubmit.normpath(files[0][1])) |
| 743 self.assertEqual(rhs_lines[1][0].LocalPath(), | 807 self.assertEqual(rhs_lines[3][0].LocalPath(), |
| 744 presubmit.normpath(files[0][1])) | 808 presubmit.normpath(files[0][1])) |
| 745 self.assertEqual(rhs_lines[2][0].LocalPath(), | 809 self.assertEqual(rhs_lines[7][0].LocalPath(), |
| 746 presubmit.normpath(files[4][1])) | 810 presubmit.normpath(files[4][1])) |
| 747 self.assertEqual(rhs_lines[3][0].LocalPath(), | 811 self.assertEqual(rhs_lines[13][0].LocalPath(), |
| 748 presubmit.normpath(files[4][1])) | 812 presubmit.normpath(files[4][1])) |
| 749 | 813 |
| 750 def testDefaultWhiteListBlackListFilters(self): | 814 def testDefaultWhiteListBlackListFilters(self): |
| 751 def f(x): | 815 def f(x): |
| 752 return presubmit.AffectedFile(x, 'M') | 816 return presubmit.AffectedFile(x, 'M') |
| 753 files = [ | 817 files = [ |
| 754 ( | 818 ( |
| 755 [ | 819 [ |
| 756 # To be tested. | 820 # To be tested. |
| 757 f('a/experimental/b'), | 821 f('a/experimental/b'), |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1011 input_buf = StringIO.StringIO('\n') | 1075 input_buf = StringIO.StringIO('\n') |
| 1012 warning = presubmit.OutputApi.PresubmitPromptWarning('???') | 1076 warning = presubmit.OutputApi.PresubmitPromptWarning('???') |
| 1013 self.failIf(warning._Handle(output, input_buf)) | 1077 self.failIf(warning._Handle(output, input_buf)) |
| 1014 self.failUnless(output.getvalue().count('???')) | 1078 self.failUnless(output.getvalue().count('???')) |
| 1015 | 1079 |
| 1016 | 1080 |
| 1017 class AffectedFileUnittest(PresubmitTestsBase): | 1081 class AffectedFileUnittest(PresubmitTestsBase): |
| 1018 def testMembersChanged(self): | 1082 def testMembersChanged(self): |
| 1019 self.mox.ReplayAll() | 1083 self.mox.ReplayAll() |
| 1020 members = [ | 1084 members = [ |
| 1021 'AbsoluteLocalPath', 'Action', 'IsDirectory', 'IsTextFile', 'LocalPath', | 1085 'AbsoluteLocalPath', 'Action', 'ChangedContents', 'IsDirectory', |
| 1022 'NewContents', 'OldContents', 'OldFileTempPath', 'Property', 'ServerPath', | 1086 'IsTextFile', 'LocalPath', 'NewContents', 'OldContents', |
| 1087 'OldFileTempPath', 'Property', 'ServerPath', |
| 1023 ] | 1088 ] |
| 1024 # If this test fails, you should add the relevant test. | 1089 # If this test fails, you should add the relevant test. |
| 1025 self.compareMembers(presubmit.AffectedFile('a', 'b'), members) | 1090 self.compareMembers(presubmit.AffectedFile('a', 'b'), members) |
| 1091 members.append('GenerateScmDiff') |
| 1026 self.compareMembers(presubmit.SvnAffectedFile('a', 'b'), members) | 1092 self.compareMembers(presubmit.SvnAffectedFile('a', 'b'), members) |
| 1027 | 1093 |
| 1028 def testAffectedFile(self): | 1094 def testAffectedFile(self): |
| 1029 path = presubmit.os.path.join('foo', 'blat.cc') | 1095 path = presubmit.os.path.join('foo', 'blat.cc') |
| 1030 presubmit.os.path.exists(path).AndReturn(True) | 1096 presubmit.os.path.exists(path).AndReturn(True) |
| 1031 presubmit.os.path.isdir(path).AndReturn(False) | 1097 presubmit.os.path.isdir(path).AndReturn(False) |
| 1032 presubmit.gclient_utils.FileRead(path, 'rU').AndReturn('whatever\ncookie') | 1098 presubmit.gclient_utils.FileRead(path, 'rU').AndReturn('whatever\ncookie') |
| 1033 presubmit.scm.SVN.CaptureInfo(path).AndReturn( | 1099 presubmit.scm.SVN.CaptureInfo(path).AndReturn( |
| 1034 {'URL': 'svn:/foo/foo/blat.cc'}) | 1100 {'URL': 'svn:/foo/foo/blat.cc'}) |
| 1035 self.mox.ReplayAll() | 1101 self.mox.ReplayAll() |
| (...skipping 726 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1762 results = presubmit_canned_checks.CheckBuildbotPendingBuilds( | 1828 results = presubmit_canned_checks.CheckBuildbotPendingBuilds( |
| 1763 input_api, presubmit.OutputApi, 'uurl', 2, ('foo')) | 1829 input_api, presubmit.OutputApi, 'uurl', 2, ('foo')) |
| 1764 self.assertEquals(len(results), 1) | 1830 self.assertEquals(len(results), 1) |
| 1765 self.assertEquals(results[0].__class__, | 1831 self.assertEquals(results[0].__class__, |
| 1766 presubmit.OutputApi.PresubmitNotifyResult) | 1832 presubmit.OutputApi.PresubmitNotifyResult) |
| 1767 | 1833 |
| 1768 | 1834 |
| 1769 if __name__ == '__main__': | 1835 if __name__ == '__main__': |
| 1770 import unittest | 1836 import unittest |
| 1771 unittest.main() | 1837 unittest.main() |
| OLD | NEW |