OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2006-2009 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 import os | 8 import os |
9 import StringIO | 9 import StringIO |
10 import sys | 10 import sys |
11 import unittest | 11 import unittest |
12 import warnings | 12 import warnings |
13 | 13 |
14 # Local imports | 14 # Local imports |
15 import __init__ | |
15 import gcl | 16 import gcl |
16 import gclient | 17 import gclient |
17 import presubmit_support as presubmit | 18 import presubmit_support as presubmit |
18 import presubmit_canned_checks | 19 import presubmit_canned_checks |
20 mox = __init__.mox | |
19 | 21 |
20 | 22 |
21 class PresubmitTestsBase(unittest.TestCase): | 23 class PresubmitTestsBase(unittest.TestCase): |
22 """Setups and tear downs the mocks but doesn't test anything as-is.""" | 24 """Setups and tear downs the mocks but doesn't test anything as-is.""" |
23 def setUp(self): | 25 def setUp(self): |
24 self._warnings_stack = warnings.catch_warnings() | 26 self._warnings_stack = warnings.catch_warnings() |
27 self.mox = mox.Mox() | |
25 warnings.simplefilter("ignore", DeprecationWarning) | 28 warnings.simplefilter("ignore", DeprecationWarning) |
26 self.original_IsFile = os.path.isfile | 29 self.original_IsFile = os.path.isfile |
27 def MockIsFile(f): | 30 def MockIsFile(f): |
28 dir = os.path.dirname(f) | 31 dir = os.path.dirname(f) |
29 return dir.endswith('haspresubmit') or dir == '' | 32 return dir.endswith('haspresubmit') or dir == '' |
30 os.path.isfile = MockIsFile | 33 os.path.isfile = MockIsFile |
31 | 34 |
32 self.original_CaptureSVNInfo = gclient.CaptureSVNInfo | 35 self.original_CaptureSVNInfo = gclient.CaptureSVNInfo |
33 def MockCaptureSVNInfo(path): | 36 def MockCaptureSVNInfo(path): |
34 if path.count('notfound'): | 37 if path.count('notfound'): |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
80 else: | 83 else: |
81 return 'one:%s\r\ntwo:%s' % (path, path) | 84 return 'one:%s\r\ntwo:%s' % (path, path) |
82 gcl.ReadFile = MockReadFile | 85 gcl.ReadFile = MockReadFile |
83 | 86 |
84 self.original_GetRepositoryRoot = gcl.GetRepositoryRoot | 87 self.original_GetRepositoryRoot = gcl.GetRepositoryRoot |
85 def MockGetRepositoryRoot(): | 88 def MockGetRepositoryRoot(): |
86 return '' | 89 return '' |
87 gcl.GetRepositoryRoot = MockGetRepositoryRoot | 90 gcl.GetRepositoryRoot = MockGetRepositoryRoot |
88 self._sys_stdout = sys.stdout | 91 self._sys_stdout = sys.stdout |
89 sys.stdout = StringIO.StringIO() | 92 sys.stdout = StringIO.StringIO() |
93 self._os_path_exists = os.path.exists | |
94 os.path.exists = self.mox.CreateMockAnything() | |
95 self._os_path_isdir = os.path.isdir | |
96 os.path.isdir = self.mox.CreateMockAnything() | |
90 | 97 |
91 def tearDown(self): | 98 def tearDown(self): |
92 os.path.isfile = self.original_IsFile | 99 os.path.isfile = self.original_IsFile |
93 gclient.CaptureSVNInfo = self.original_CaptureSVNInfo | 100 gclient.CaptureSVNInfo = self.original_CaptureSVNInfo |
94 gcl.GetSVNFileProperty = self.original_GetSVNFileProperty | 101 gcl.GetSVNFileProperty = self.original_GetSVNFileProperty |
95 gcl.ReadFile = self.original_ReadFile | 102 gcl.ReadFile = self.original_ReadFile |
96 gcl.GetRepositoryRoot = self.original_GetRepositoryRoot | 103 gcl.GetRepositoryRoot = self.original_GetRepositoryRoot |
97 sys.stdout = self._sys_stdout | 104 sys.stdout = self._sys_stdout |
105 os.path.exists = self._os_path_exists | |
106 os.path.isdir = self._os_path_isdir | |
98 self._warnings_stack = None | 107 self._warnings_stack = None |
99 | 108 |
100 @staticmethod | 109 @staticmethod |
101 def MakeBasicChange(name, description): | 110 def MakeBasicChange(name, description): |
102 ci = gcl.ChangeInfo(name=name, | 111 ci = gcl.ChangeInfo(name=name, |
103 description=description, | 112 description=description, |
104 files=[]) | 113 files=[]) |
105 change = presubmit.GclChange(ci) | 114 change = presubmit.GclChange(ci) |
106 return change | 115 return change |
107 | 116 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
153 'BUG=123', | 162 'BUG=123', |
154 ' STORY =http://foo/ \t', | 163 ' STORY =http://foo/ \t', |
155 'and some more regular text \t') | 164 'and some more regular text \t') |
156 files = [ | 165 files = [ |
157 ['A', 'foo/blat.cc'], | 166 ['A', 'foo/blat.cc'], |
158 ['M', 'binary.dll'], # a binary file | 167 ['M', 'binary.dll'], # a binary file |
159 ['A', 'isdir'], # a directory | 168 ['A', 'isdir'], # a directory |
160 ['M', 'flop/notfound.txt'], # not found in SVN, still exists locally | 169 ['M', 'flop/notfound.txt'], # not found in SVN, still exists locally |
161 ['D', 'boo/flap.h'], | 170 ['D', 'boo/flap.h'], |
162 ] | 171 ] |
163 | 172 os.path.exists(os.path.join('foo', 'blat.cc')).AndReturn(True) |
173 os.path.isdir(os.path.join('foo', 'blat.cc')).AndReturn(False) | |
174 os.path.exists('binary.dll').AndReturn(True) | |
175 os.path.isdir('binary.dll').AndReturn(False) | |
176 os.path.exists('isdir').AndReturn(True) | |
177 os.path.isdir('isdir').AndReturn(True) | |
178 os.path.exists(os.path.join('flop', 'notfound.txt')).AndReturn(False) | |
179 os.path.exists(os.path.join('boo', 'flap.h')).AndReturn(False) | |
180 self.mox.ReplayAll() | |
164 ci = gcl.ChangeInfo(name='mychange', | 181 ci = gcl.ChangeInfo(name='mychange', |
165 description='\n'.join(description_lines), | 182 description='\n'.join(description_lines), |
166 files=files) | 183 files=files) |
167 change = presubmit.GclChange(ci) | 184 change = presubmit.GclChange(ci) |
168 | 185 |
169 self.failUnless(change.Change() == 'mychange') | 186 self.failUnless(change.Change() == 'mychange') |
170 self.failUnless(change.DescriptionText() == | 187 self.failUnless(change.DescriptionText() == |
171 'Hello there\nthis is a change\nand some more regular text') | 188 'Hello there\nthis is a change\nand some more regular text') |
172 self.failUnless(change.FullDescriptionText() == | 189 self.failUnless(change.FullDescriptionText() == |
173 '\n'.join(description_lines)) | 190 '\n'.join(description_lines)) |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
211 self.failUnless(rhs_lines[1][1] == 2) | 228 self.failUnless(rhs_lines[1][1] == 2) |
212 self.failUnless(rhs_lines[1][2] == 'two:%s' % files[0][1]) | 229 self.failUnless(rhs_lines[1][2] == 'two:%s' % files[0][1]) |
213 | 230 |
214 self.failUnless(rhs_lines[2][0].LocalPath() == files[3][1]) | 231 self.failUnless(rhs_lines[2][0].LocalPath() == files[3][1]) |
215 self.failUnless(rhs_lines[2][1] == 1) | 232 self.failUnless(rhs_lines[2][1] == 1) |
216 self.failUnless(rhs_lines[2][2] == 'one:%s' % files[3][1]) | 233 self.failUnless(rhs_lines[2][2] == 'one:%s' % files[3][1]) |
217 | 234 |
218 self.failUnless(rhs_lines[3][0].LocalPath() == files[3][1]) | 235 self.failUnless(rhs_lines[3][0].LocalPath() == files[3][1]) |
219 self.failUnless(rhs_lines[3][1] == 2) | 236 self.failUnless(rhs_lines[3][1] == 2) |
220 self.failUnless(rhs_lines[3][2] == 'two:%s' % files[3][1]) | 237 self.failUnless(rhs_lines[3][2] == 'two:%s' % files[3][1]) |
238 self.mox.VerifyAll() | |
221 | 239 |
222 def testExecPresubmitScript(self): | 240 def testExecPresubmitScript(self): |
223 description_lines = ('Hello there', | 241 description_lines = ('Hello there', |
224 'this is a change', | 242 'this is a change', |
225 'STORY=http://tracker/123') | 243 'STORY=http://tracker/123') |
226 files = [ | 244 files = [ |
227 ['A', 'foo\\blat.cc'], | 245 ['A', 'foo\\blat.cc'], |
228 ] | 246 ] |
229 ci = gcl.ChangeInfo(name='mychange', | 247 ci = gcl.ChangeInfo(name='mychange', |
230 description='\n'.join(description_lines), | 248 description='\n'.join(description_lines), |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
380 os.path.isfile = MockIsFile | 398 os.path.isfile = MockIsFile |
381 self.failIf(presubmit.DoPresubmitChecks(ci, False, True, output, input, | 399 self.failIf(presubmit.DoPresubmitChecks(ci, False, True, output, input, |
382 DEFAULT_SCRIPT)) | 400 DEFAULT_SCRIPT)) |
383 self.assertEquals(output.getvalue().count('!!'), 1) | 401 self.assertEquals(output.getvalue().count('!!'), 1) |
384 | 402 |
385 def testDirectoryHandling(self): | 403 def testDirectoryHandling(self): |
386 files = [ | 404 files = [ |
387 ['A', 'isdir'], | 405 ['A', 'isdir'], |
388 ['A', 'isdir\\blat.cc'], | 406 ['A', 'isdir\\blat.cc'], |
389 ] | 407 ] |
408 os.path.exists('isdir').AndReturn(True) | |
409 os.path.isdir('isdir').AndReturn(True) | |
410 os.path.exists(os.path.join('isdir', 'blat.cc')).AndReturn(True) | |
411 os.path.isdir(os.path.join('isdir', 'blat.cc')).AndReturn(False) | |
412 self.mox.ReplayAll() | |
390 ci = gcl.ChangeInfo(name='mychange', | 413 ci = gcl.ChangeInfo(name='mychange', |
391 description='foo', | 414 description='foo', |
392 files=files) | 415 files=files) |
393 change = presubmit.GclChange(ci) | 416 change = presubmit.GclChange(ci) |
394 | 417 |
395 affected_files = change.AffectedFiles(include_dirs=False) | 418 affected_files = change.AffectedFiles(include_dirs=False) |
396 self.failUnless(len(affected_files) == 1) | 419 self.failUnless(len(affected_files) == 1) |
397 self.failUnless(affected_files[0].LocalPath().endswith('blat.cc')) | 420 self.failUnless(affected_files[0].LocalPath().endswith('blat.cc')) |
398 | 421 self.mox.VerifyAll() |
399 affected_files_and_dirs = change.AffectedFiles(include_dirs=True) | 422 affected_files_and_dirs = change.AffectedFiles(include_dirs=True) |
400 self.failUnless(len(affected_files_and_dirs) == 2) | 423 self.failUnless(len(affected_files_and_dirs) == 2) |
401 | 424 |
402 def testTags(self): | 425 def testTags(self): |
403 DEFAULT_SCRIPT = """ | 426 DEFAULT_SCRIPT = """ |
404 def CheckChangeOnUpload(input_api, output_api): | 427 def CheckChangeOnUpload(input_api, output_api): |
405 if input_api.change.tags['BUG'] != 'boo': | 428 if input_api.change.tags['BUG'] != 'boo': |
406 return [output_api.PresubmitError('Tag parsing failed. 1')] | 429 return [output_api.PresubmitError('Tag parsing failed. 1')] |
407 if input_api.change.tags['STORY'] != 'http://tracker.com/42': | 430 if input_api.change.tags['STORY'] != 'http://tracker.com/42': |
408 return [output_api.PresubmitError('Tag parsing failed. 2')] | 431 return [output_api.PresubmitError('Tag parsing failed. 2')] |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
503 ' STORY =http://foo/ \t', | 526 ' STORY =http://foo/ \t', |
504 'and some more regular text') | 527 'and some more regular text') |
505 files = [ | 528 files = [ |
506 ['A', os.path.join('foo', 'blat.cc')], | 529 ['A', os.path.join('foo', 'blat.cc')], |
507 ['M', os.path.join('foo', 'blat', 'binary.dll')], | 530 ['M', os.path.join('foo', 'blat', 'binary.dll')], |
508 ['D', 'foo/mat/beingdeleted.txt'], | 531 ['D', 'foo/mat/beingdeleted.txt'], |
509 ['M', 'flop/notfound.txt'], | 532 ['M', 'flop/notfound.txt'], |
510 ['A', 'boo/flap.h'], | 533 ['A', 'boo/flap.h'], |
511 ] | 534 ] |
512 | 535 |
536 os.path.exists(os.path.join('foo', 'blat.cc')).AndReturn(True) | |
537 os.path.isdir(os.path.join('foo', 'blat.cc')).AndReturn(False) | |
538 os.path.exists(os.path.join('foo', 'blat', 'binary.dll')).AndReturn(True) | |
539 os.path.isdir(os.path.join('foo', 'blat', 'binary.dll')).AndReturn(False) | |
540 os.path.exists(os.path.join('foo', 'mat', 'beingdeleted.txt')).AndReturn( | |
541 False) | |
542 os.path.exists(os.path.join('flop', 'notfound.txt')).AndReturn(False) | |
543 os.path.exists(os.path.join('boo', 'flap.h')).AndReturn(True) | |
544 os.path.isdir(os.path.join('boo', 'flap.h')).AndReturn(False) | |
545 self.mox.ReplayAll() | |
513 ci = gcl.ChangeInfo(name='mychange', | 546 ci = gcl.ChangeInfo(name='mychange', |
514 description='\n'.join(description_lines), | 547 description='\n'.join(description_lines), |
515 files=files) | 548 files=files) |
516 change = presubmit.GclChange(ci) | 549 change = presubmit.GclChange(ci) |
517 | 550 |
518 api = presubmit.InputApi(change, 'foo/PRESUBMIT.py') | 551 api = presubmit.InputApi(change, 'foo/PRESUBMIT.py') |
519 | 552 |
520 affected_files = api.AffectedFiles() | 553 affected_files = api.AffectedFiles() |
521 self.assertEquals(len(affected_files), 3) | 554 self.assertEquals(len(affected_files), 3) |
522 self.assertEquals(affected_files[0].LocalPath(), | 555 self.assertEquals(affected_files[0].LocalPath(), |
523 presubmit.normpath('foo/blat.cc')) | 556 presubmit.normpath('foo/blat.cc')) |
524 self.assertEquals(affected_files[1].LocalPath(), | 557 self.assertEquals(affected_files[1].LocalPath(), |
525 presubmit.normpath('foo/blat/binary.dll')) | 558 presubmit.normpath('foo/blat/binary.dll')) |
526 self.assertEquals(affected_files[2].LocalPath(), | 559 self.assertEquals(affected_files[2].LocalPath(), |
527 presubmit.normpath('foo/mat/beingdeleted.txt')) | 560 presubmit.normpath('foo/mat/beingdeleted.txt')) |
528 | 561 |
529 rhs_lines = [] | 562 rhs_lines = [] |
530 for line in api.RightHandSideLines(): | 563 for line in api.RightHandSideLines(): |
531 rhs_lines.append(line) | 564 rhs_lines.append(line) |
532 self.failUnless(len(rhs_lines) == 2) | 565 self.failUnless(len(rhs_lines) == 2) |
533 self.assertEqual(rhs_lines[0][0].LocalPath(), | 566 self.assertEqual(rhs_lines[0][0].LocalPath(), |
534 presubmit.normpath('foo/blat.cc')) | 567 presubmit.normpath('foo/blat.cc')) |
568 self.mox.VerifyAll() | |
535 | 569 |
536 def testGetAbsoluteLocalPath(self): | 570 def testGetAbsoluteLocalPath(self): |
537 # Regression test for bug of presubmit stuff that relies on invoking | 571 # Regression test for bug of presubmit stuff that relies on invoking |
538 # SVN (e.g. to get mime type of file) not working unless gcl invoked | 572 # SVN (e.g. to get mime type of file) not working unless gcl invoked |
539 # from the client root (e.g. if you were at 'src' and did 'cd base' before | 573 # from the client root (e.g. if you were at 'src' and did 'cd base' before |
540 # invoking 'gcl upload' it would fail because svn wouldn't find the files | 574 # invoking 'gcl upload' it would fail because svn wouldn't find the files |
541 # the presubmit script was asking about). | 575 # the presubmit script was asking about). |
542 files = [ | 576 files = [ |
543 ['A', 'isdir'], | 577 ['A', 'isdir'], |
544 ['A', os.path.join('isdir', 'blat.cc')] | 578 ['A', os.path.join('isdir', 'blat.cc')] |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
622 output = StringIO.StringIO() | 656 output = StringIO.StringIO() |
623 input = StringIO.StringIO('\n') | 657 input = StringIO.StringIO('\n') |
624 warning = presubmit.OutputApi.PresubmitPromptWarning('???') | 658 warning = presubmit.OutputApi.PresubmitPromptWarning('???') |
625 self.failIf(warning._Handle(output, input)) | 659 self.failIf(warning._Handle(output, input)) |
626 self.failUnless(output.getvalue().count('???')) | 660 self.failUnless(output.getvalue().count('???')) |
627 | 661 |
628 | 662 |
629 class AffectedFileUnittest(PresubmitTestsBase): | 663 class AffectedFileUnittest(PresubmitTestsBase): |
630 def testMembersChanged(self): | 664 def testMembersChanged(self): |
631 members = [ | 665 members = [ |
632 'AbsoluteLocalPath', 'Action', 'IsDirectory', 'IsTextFile', | 666 'AbsoluteLocalPath', 'Action', 'IsDirectory', 'IsTextFile', 'LocalPath', |
633 'LocalPath', 'NewContents', | 667 'NewContents', 'OldContents', 'OldFileTempPath', 'Property', 'ServerPath', |
634 'OldContents', 'OldFileTempPath', 'Property', 'ServerPath', 'action', | |
635 'is_directory', 'path', 'properties', 'repository_root', 'server_path', | |
636 ] | 668 ] |
637 # If this test fails, you should add the relevant test. | 669 # If this test fails, you should add the relevant test. |
638 self.compareMembers(presubmit.AffectedFile('a', 'b'), members) | 670 self.compareMembers(presubmit.AffectedFile('a', 'b'), members) |
639 self.compareMembers(presubmit.SvnAffectedFile('a', 'b'), members) | 671 self.compareMembers(presubmit.SvnAffectedFile('a', 'b'), members) |
640 | 672 |
641 def testAffectedFile(self): | 673 def testAffectedFile(self): |
642 af = presubmit.SvnAffectedFile('foo/blat.cc', 'M') | 674 af = presubmit.SvnAffectedFile('foo/blat.cc', 'M') |
675 os.path.exists(os.path.join('foo', 'blat.cc')).AndReturn(False) | |
676 self.mox.ReplayAll() | |
643 self.failUnless(af.ServerPath() == 'svn:/foo/foo/blat.cc') | 677 self.failUnless(af.ServerPath() == 'svn:/foo/foo/blat.cc') |
644 self.failUnless(af.LocalPath() == presubmit.normpath('foo/blat.cc')) | 678 self.failUnless(af.LocalPath() == presubmit.normpath('foo/blat.cc')) |
645 self.failUnless(af.Action() == 'M') | 679 self.failUnless(af.Action() == 'M') |
646 self.failUnless(af.NewContents() == ['one:%s' % af.LocalPath(), | 680 self.failUnless(af.NewContents() == ['one:%s' % af.LocalPath(), |
647 'two:%s' % af.LocalPath()]) | 681 'two:%s' % af.LocalPath()]) |
682 self.mox.VerifyAll() | |
648 | 683 |
649 af = presubmit.AffectedFile('notfound.cc', 'A') | 684 af = presubmit.AffectedFile('notfound.cc', 'A') |
650 self.failUnless(af.ServerPath() == '') | 685 self.failUnless(af.ServerPath() == '') |
651 | 686 |
652 def testProperty(self): | 687 def testProperty(self): |
653 affected_file = presubmit.SvnAffectedFile('foo.cc', 'A') | 688 affected_file = presubmit.SvnAffectedFile('foo.cc', 'A') |
654 self.failUnless(affected_file.Property('svn:secret-property') == | 689 self.failUnless(affected_file.Property('svn:secret-property') == |
655 'secret-property-value') | 690 'secret-property-value') |
656 | 691 |
692 def testIsDirectoryNotExists(self): | |
693 # Verify cache coherency | |
694 os.path.exists('foo.cc').AndReturn(False) | |
695 self.mox.ReplayAll() | |
696 affected_file = presubmit.SvnAffectedFile('foo.cc', 'A') | |
697 self.failIf(affected_file.IsDirectory()) | |
698 self.failIf(affected_file.IsDirectory()) | |
Jói Sigurðsson
2009/05/28 21:30:51
duplicate?
M-A Ruel
2009/05/29 00:59:55
Yes, that's to verify the cache coherency. I've mo
| |
699 self.mox.VerifyAll() | |
700 | |
701 def testIsDirectory(self): | |
702 # Verify cache coherency | |
703 os.path.exists('foo.cc').AndReturn(True) | |
704 os.path.isdir('foo.cc').AndReturn(True) | |
705 self.mox.ReplayAll() | |
706 affected_file = presubmit.SvnAffectedFile('foo.cc', 'A') | |
707 self.failUnless(affected_file.IsDirectory()) | |
708 self.failUnless(affected_file.IsDirectory()) | |
Jói Sigurðsson
2009/05/28 21:30:51
duplicate?
| |
709 self.mox.VerifyAll() | |
710 | |
657 | 711 |
658 class CannedChecksUnittest(PresubmitTestsBase): | 712 class CannedChecksUnittest(PresubmitTestsBase): |
659 """Tests presubmit_canned_checks.py.""" | 713 """Tests presubmit_canned_checks.py.""" |
660 class MockInputApi(object): | 714 class MockInputApi(object): |
661 class MockUrllib2(object): | 715 class MockUrllib2(object): |
662 class urlopen(object): | 716 class urlopen(object): |
663 def __init__(self, url): | 717 def __init__(self, url): |
664 if url == 'url_to_open': | 718 if url == 'url_to_open': |
665 self.result = '1' | 719 self.result = '1' |
666 else: | 720 else: |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
790 # TODO(maruel): Add real tests. | 844 # TODO(maruel): Add real tests. |
791 self.failIf(presubmit_canned_checks.RunPythonUnitTests( | 845 self.failIf(presubmit_canned_checks.RunPythonUnitTests( |
792 self.MockInputApi(), | 846 self.MockInputApi(), |
793 presubmit.OutputApi, [])) | 847 presubmit.OutputApi, [])) |
794 self.failUnless(presubmit_canned_checks.RunPythonUnitTests( | 848 self.failUnless(presubmit_canned_checks.RunPythonUnitTests( |
795 self.MockInputApi(), | 849 self.MockInputApi(), |
796 presubmit.OutputApi, ['non_existent_module'])) | 850 presubmit.OutputApi, ['non_existent_module'])) |
797 | 851 |
798 if __name__ == '__main__': | 852 if __name__ == '__main__': |
799 unittest.main() | 853 unittest.main() |
OLD | NEW |