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.py and presubmit_canned_checks.py.""" | 6 """Unit tests for presubmit.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 unittest | 11 import unittest |
11 | 12 |
12 # Local imports | 13 # Local imports |
13 import gcl | 14 import gcl |
14 import gclient | 15 import gclient |
15 import presubmit_support as presubmit | 16 import presubmit_support as presubmit |
16 import presubmit_canned_checks | 17 import presubmit_canned_checks |
17 | 18 |
18 | 19 |
19 class PresubmitTestsBase(unittest.TestCase): | 20 class PresubmitTestsBase(unittest.TestCase): |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 ' else:\n' | 73 ' else:\n' |
73 ' return ()') | 74 ' return ()') |
74 else: | 75 else: |
75 return 'one:%s\r\ntwo:%s' % (path, path) | 76 return 'one:%s\r\ntwo:%s' % (path, path) |
76 gcl.ReadFile = MockReadFile | 77 gcl.ReadFile = MockReadFile |
77 | 78 |
78 self.original_GetRepositoryRoot = gcl.GetRepositoryRoot | 79 self.original_GetRepositoryRoot = gcl.GetRepositoryRoot |
79 def MockGetRepositoryRoot(): | 80 def MockGetRepositoryRoot(): |
80 return '' | 81 return '' |
81 gcl.GetRepositoryRoot = MockGetRepositoryRoot | 82 gcl.GetRepositoryRoot = MockGetRepositoryRoot |
| 83 self._sys_stdout = sys.stdout |
| 84 sys.stdout = StringIO.StringIO() |
82 | 85 |
83 def tearDown(self): | 86 def tearDown(self): |
84 os.path.isfile = self.original_IsFile | 87 os.path.isfile = self.original_IsFile |
85 gclient.CaptureSVNInfo = self.original_CaptureSVNInfo | 88 gclient.CaptureSVNInfo = self.original_CaptureSVNInfo |
86 gcl.GetSVNFileProperty = self.original_GetSVNFileProperty | 89 gcl.GetSVNFileProperty = self.original_GetSVNFileProperty |
87 gcl.ReadFile = self.original_ReadFile | 90 gcl.ReadFile = self.original_ReadFile |
88 gcl.GetRepositoryRoot = self.original_GetRepositoryRoot | 91 gcl.GetRepositoryRoot = self.original_GetRepositoryRoot |
| 92 sys.stdout = self._sys_stdout |
89 | 93 |
90 @staticmethod | 94 @staticmethod |
91 def MakeBasicChange(name, description): | 95 def MakeBasicChange(name, description): |
92 ci = gcl.ChangeInfo(name=name, | 96 ci = gcl.ChangeInfo(name=name, |
93 description=description, | 97 description=description, |
94 files=[]) | 98 files=[]) |
95 change = presubmit.GclChange(ci) | 99 change = presubmit.GclChange(ci) |
96 return change | 100 return change |
97 | 101 |
98 def compareMembers(self, object, members): | 102 def compareMembers(self, object, members): |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
292 files = [ | 296 files = [ |
293 ['A', 'haspresubmit\\blat.cc'], | 297 ['A', 'haspresubmit\\blat.cc'], |
294 ] | 298 ] |
295 ci = gcl.ChangeInfo(name='mychange', | 299 ci = gcl.ChangeInfo(name='mychange', |
296 description='\n'.join(description_lines), | 300 description='\n'.join(description_lines), |
297 files=files) | 301 files=files) |
298 | 302 |
299 output = StringIO.StringIO() | 303 output = StringIO.StringIO() |
300 input = StringIO.StringIO('y\n') | 304 input = StringIO.StringIO('y\n') |
301 | 305 |
302 self.failIf(presubmit.DoPresubmitChecks(ci, False, False, output, input)) | 306 self.failIf(presubmit.DoPresubmitChecks(ci, False, True, output, input)) |
303 self.failUnless(output.getvalue().count('!!')) | 307 self.failUnless(output.getvalue().count('!!')) |
304 | 308 |
305 def testDoPresubmitChecksPromptsAfterWarnings(self): | 309 def testDoPresubmitChecksPromptsAfterWarnings(self): |
306 description_lines = ('Hello there', | 310 description_lines = ('Hello there', |
307 'this is a change', | 311 'this is a change', |
308 'NOSUCHKEY=http://tracker/123') | 312 'NOSUCHKEY=http://tracker/123') |
309 files = [ | 313 files = [ |
310 ['A', 'haspresubmit\\blat.cc'], | 314 ['A', 'haspresubmit\\blat.cc'], |
311 ] | 315 ] |
312 ci = gcl.ChangeInfo(name='mychange', | 316 ci = gcl.ChangeInfo(name='mychange', |
313 description='\n'.join(description_lines), | 317 description='\n'.join(description_lines), |
314 files=files) | 318 files=files) |
315 | 319 |
316 output = StringIO.StringIO() | 320 output = StringIO.StringIO() |
317 input = StringIO.StringIO('n\n') # say no to the warning | 321 input = StringIO.StringIO('n\n') # say no to the warning |
318 | 322 |
319 self.failIf(presubmit.DoPresubmitChecks(ci, False, False, output, input)) | 323 self.failIf(presubmit.DoPresubmitChecks(ci, False, True, output, input)) |
320 self.failUnless(output.getvalue().count('??')) | 324 self.failUnless(output.getvalue().count('??')) |
321 | 325 |
322 output = StringIO.StringIO() | 326 output = StringIO.StringIO() |
323 input = StringIO.StringIO('y\n') # say yes to the warning | 327 input = StringIO.StringIO('y\n') # say yes to the warning |
324 | 328 |
325 self.failUnless(presubmit.DoPresubmitChecks(ci, | 329 self.failUnless(presubmit.DoPresubmitChecks(ci, |
326 False, | 330 False, |
327 False, | 331 True, |
328 output, | 332 output, |
329 input)) | 333 input)) |
330 self.failUnless(output.getvalue().count('??')) | 334 self.failUnless(output.getvalue().count('??')) |
331 | 335 |
332 def testDoPresubmitChecksNoWarningPromptIfErrors(self): | 336 def testDoPresubmitChecksNoWarningPromptIfErrors(self): |
333 description_lines = ('Hello there', | 337 description_lines = ('Hello there', |
334 'this is a change', | 338 'this is a change', |
335 'NOSUCHKEY=http://tracker/123', | 339 'NOSUCHKEY=http://tracker/123', |
336 'REALLYNOSUCHKEY=http://tracker/123') | 340 'REALLYNOSUCHKEY=http://tracker/123') |
337 files = [ | 341 files = [ |
338 ['A', 'haspresubmit\\blat.cc'], | 342 ['A', 'haspresubmit\\blat.cc'], |
339 ] | 343 ] |
340 ci = gcl.ChangeInfo(name='mychange', | 344 ci = gcl.ChangeInfo(name='mychange', |
341 description='\n'.join(description_lines), | 345 description='\n'.join(description_lines), |
342 files=files) | 346 files=files) |
343 | 347 |
344 output = StringIO.StringIO() | 348 output = StringIO.StringIO() |
345 input = StringIO.StringIO() # should be unused | 349 input = StringIO.StringIO() # should be unused |
346 | 350 |
347 self.failIf(presubmit.DoPresubmitChecks(ci, False, False, output, input)) | 351 self.failIf(presubmit.DoPresubmitChecks(ci, False, True, output, input)) |
348 self.failUnless(output.getvalue().count('??')) | 352 self.failUnless(output.getvalue().count('??')) |
349 self.failUnless(output.getvalue().count('XX!!XX')) | 353 self.failUnless(output.getvalue().count('XX!!XX')) |
350 self.failIf(output.getvalue().count('(y/N)')) | 354 self.failIf(output.getvalue().count('(y/N)')) |
351 | 355 |
352 def testDirectoryHandling(self): | 356 def testDirectoryHandling(self): |
353 files = [ | 357 files = [ |
354 ['A', 'isdir'], | 358 ['A', 'isdir'], |
355 ['A', 'isdir\\blat.cc'], | 359 ['A', 'isdir\\blat.cc'], |
356 ] | 360 ] |
357 ci = gcl.ChangeInfo(name='mychange', | 361 ci = gcl.ChangeInfo(name='mychange', |
(...skipping 15 matching lines...) Expand all Loading... |
373 | 377 |
374 | 378 |
375 class InputApiUnittest(PresubmitTestsBase): | 379 class InputApiUnittest(PresubmitTestsBase): |
376 """Tests presubmit.InputApi.""" | 380 """Tests presubmit.InputApi.""" |
377 def testMembersChanged(self): | 381 def testMembersChanged(self): |
378 members = [ | 382 members = [ |
379 'AbsoluteLocalPaths', 'AffectedFiles', 'AffectedTextFiles', | 383 'AbsoluteLocalPaths', 'AffectedFiles', 'AffectedTextFiles', |
380 'DepotToLocalPath', 'FilterTextFiles', 'LocalPaths', 'LocalToDepotPath', | 384 'DepotToLocalPath', 'FilterTextFiles', 'LocalPaths', 'LocalToDepotPath', |
381 'PresubmitLocalPath', 'RightHandSideLines', 'ServerPaths', | 385 'PresubmitLocalPath', 'RightHandSideLines', 'ServerPaths', |
382 'basename', 'cPickle', 'cStringIO', 'canned_checks', 'change', | 386 'basename', 'cPickle', 'cStringIO', 'canned_checks', 'change', |
383 'current_presubmit_path', 'marshal', 'os_path', 'pickle', 'platform', | 387 'marshal', 'os_path', 'pickle', 'platform', |
384 're', 'subprocess', 'tempfile', 'urllib2', | 388 're', 'subprocess', 'tempfile', 'urllib2', |
385 ] | 389 ] |
386 # If this test fails, you should add the relevant test. | 390 # If this test fails, you should add the relevant test. |
387 self.compareMembers(presubmit.InputApi(None, None), members) | 391 self.compareMembers(presubmit.InputApi(None, './.'), members) |
388 | 392 |
389 def testDepotToLocalPath(self): | 393 def testDepotToLocalPath(self): |
390 path = presubmit.InputApi.DepotToLocalPath('svn:/foo/smurf') | 394 path = presubmit.InputApi.DepotToLocalPath('svn:/foo/smurf') |
391 self.failUnless(path == 'smurf') | 395 self.failUnless(path == 'smurf') |
392 path = presubmit.InputApi.DepotToLocalPath('svn:/foo/notfound/burp') | 396 path = presubmit.InputApi.DepotToLocalPath('svn:/foo/notfound/burp') |
393 self.failUnless(path == None) | 397 self.failUnless(path == None) |
394 | 398 |
395 def testLocalToDepotPath(self): | 399 def testLocalToDepotPath(self): |
396 path = presubmit.InputApi.LocalToDepotPath('smurf') | 400 path = presubmit.InputApi.LocalToDepotPath('smurf') |
397 self.failUnless(path == 'svn:/foo/smurf') | 401 self.failUnless(path == 'svn:/foo/smurf') |
398 path = presubmit.InputApi.LocalToDepotPath('notfound-food') | 402 path = presubmit.InputApi.LocalToDepotPath('notfound-food') |
399 self.failUnless(path == None) | 403 self.failUnless(path == None) |
400 | 404 |
401 def testInputApiConstruction(self): | 405 def testInputApiConstruction(self): |
402 # Fudge the change object, it's not used during construction anyway | 406 # Fudge the change object, it's not used during construction anyway |
403 api = presubmit.InputApi(change=42, presubmit_path='foo/path') | 407 api = presubmit.InputApi(change=42, presubmit_path='foo/path/PRESUBMIT.py') |
404 self.failUnless(api.PresubmitLocalPath() == 'foo/path') | 408 self.failUnless(api.PresubmitLocalPath() == 'foo/path') |
405 self.failUnless(api.change == 42) | 409 self.failUnless(api.change == 42) |
406 | 410 |
407 def testFilterTextFiles(self): | 411 def testFilterTextFiles(self): |
408 class MockAffectedFile(object): | 412 class MockAffectedFile(object): |
409 def __init__(self, path, action): | 413 def __init__(self, path, action): |
410 self.path = path | 414 self.path = path |
411 self.action = action | 415 self.action = action |
412 def Action(self): | 416 def Action(self): |
413 return self.action | 417 return self.action |
(...skipping 29 matching lines...) Expand all Loading... |
443 ] | 447 ] |
444 | 448 |
445 ci = gcl.ChangeInfo(name='mychange', | 449 ci = gcl.ChangeInfo(name='mychange', |
446 description='\n'.join(description_lines), | 450 description='\n'.join(description_lines), |
447 files=files) | 451 files=files) |
448 change = presubmit.GclChange(ci) | 452 change = presubmit.GclChange(ci) |
449 | 453 |
450 api = presubmit.InputApi(change, 'foo/PRESUBMIT.py') | 454 api = presubmit.InputApi(change, 'foo/PRESUBMIT.py') |
451 | 455 |
452 affected_files = api.AffectedFiles() | 456 affected_files = api.AffectedFiles() |
453 self.failUnless(len(affected_files) == 3) | 457 self.assertEquals(len(affected_files), 3) |
454 self.failUnless(affected_files[0].LocalPath() == | 458 self.assertEquals(affected_files[0].LocalPath(), |
455 presubmit.normpath('foo/blat.cc')) | 459 presubmit.normpath('foo/blat.cc')) |
456 self.failUnless(affected_files[1].LocalPath() == | 460 self.assertEquals(affected_files[1].LocalPath(), |
457 presubmit.normpath('foo/blat/binary.dll')) | 461 presubmit.normpath('foo/blat/binary.dll')) |
458 self.failUnless(affected_files[2].LocalPath() == | 462 self.assertEquals(affected_files[2].LocalPath(), |
459 presubmit.normpath('foo/mat/beingdeleted.txt')) | 463 presubmit.normpath('foo/mat/beingdeleted.txt')) |
460 | 464 |
461 rhs_lines = [] | 465 rhs_lines = [] |
462 for line in api.RightHandSideLines(): | 466 for line in api.RightHandSideLines(): |
463 rhs_lines.append(line) | 467 rhs_lines.append(line) |
464 self.failUnless(len(rhs_lines) == 2) | 468 self.failUnless(len(rhs_lines) == 2) |
465 self.failUnless(rhs_lines[0][0].LocalPath() == | 469 self.failUnless(rhs_lines[0][0].LocalPath() == |
466 presubmit.normpath('foo/blat.cc')) | 470 presubmit.normpath('foo/blat.cc')) |
467 | 471 |
468 def testGetAbsoluteLocalPath(self): | 472 def testGetAbsoluteLocalPath(self): |
469 # Regression test for bug of presubmit stuff that relies on invoking | 473 # Regression test for bug of presubmit stuff that relies on invoking |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 'CheckChangeHasTestField', 'CheckDoNotSubmit', | 593 'CheckChangeHasTestField', 'CheckDoNotSubmit', |
590 'CheckDoNotSubmitInDescription', 'CheckDoNotSubmitInFiles', | 594 'CheckDoNotSubmitInDescription', 'CheckDoNotSubmitInFiles', |
591 'CheckLongLines', 'CheckTreeIsOpen', | 595 'CheckLongLines', 'CheckTreeIsOpen', |
592 ] | 596 ] |
593 # If this test fails, you should add the relevant test. | 597 # If this test fails, you should add the relevant test. |
594 self.compareMembers(presubmit_canned_checks, members) | 598 self.compareMembers(presubmit_canned_checks, members) |
595 | 599 |
596 def testCannedCheckChangeHasBugField(self): | 600 def testCannedCheckChangeHasBugField(self): |
597 change = self.MakeBasicChange('foo', | 601 change = self.MakeBasicChange('foo', |
598 'Foo\nBUG=1234') | 602 'Foo\nBUG=1234') |
599 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 603 api = presubmit.InputApi(change, './PRESUBMIT.py') |
600 self.failIf(presubmit_canned_checks.CheckChangeHasBugField( | 604 self.failIf(presubmit_canned_checks.CheckChangeHasBugField( |
601 api, presubmit.OutputApi)) | 605 api, presubmit.OutputApi)) |
602 | 606 |
603 change = self.MakeBasicChange('foo', | 607 change = self.MakeBasicChange('foo', |
604 'Foo\nNEVERTESTED=did some stuff') | 608 'Foo\nNEVERTESTED=did some stuff') |
605 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 609 api = presubmit.InputApi(change, './PRESUBMIT.py') |
606 self.failUnless(presubmit_canned_checks.CheckChangeHasBugField( | 610 self.failUnless(presubmit_canned_checks.CheckChangeHasBugField( |
607 api, presubmit.OutputApi)) | 611 api, presubmit.OutputApi)) |
608 | 612 |
609 def testCannedCheckChangeHasTestField(self): | 613 def testCannedCheckChangeHasTestField(self): |
610 change = self.MakeBasicChange('foo', | 614 change = self.MakeBasicChange('foo', |
611 'Foo\nTEST=did some stuff') | 615 'Foo\nTEST=did some stuff') |
612 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 616 api = presubmit.InputApi(change, './PRESUBMIT.py') |
613 self.failIf(presubmit_canned_checks.CheckChangeHasTestField( | 617 self.failIf(presubmit_canned_checks.CheckChangeHasTestField( |
614 api, presubmit.OutputApi)) | 618 api, presubmit.OutputApi)) |
615 | 619 |
616 change = self.MakeBasicChange('foo', | 620 change = self.MakeBasicChange('foo', |
617 'Foo\nNOTEST=did some stuff') | 621 'Foo\nNOTEST=did some stuff') |
618 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 622 api = presubmit.InputApi(change, './PRESUBMIT.py') |
619 self.failUnless(presubmit_canned_checks.CheckChangeHasTestField( | 623 self.failUnless(presubmit_canned_checks.CheckChangeHasTestField( |
620 api, presubmit.OutputApi)) | 624 api, presubmit.OutputApi)) |
621 | 625 |
622 def testCannedCheckChangeHasTestedField(self): | 626 def testCannedCheckChangeHasTestedField(self): |
623 change = self.MakeBasicChange('foo', | 627 change = self.MakeBasicChange('foo', |
624 'Foo\nTESTED=did some stuff') | 628 'Foo\nTESTED=did some stuff') |
625 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 629 api = presubmit.InputApi(change, './PRESUBMIT.py') |
626 self.failIf(presubmit_canned_checks.CheckChangeHasTestedField( | 630 self.failIf(presubmit_canned_checks.CheckChangeHasTestedField( |
627 api, presubmit.OutputApi)) | 631 api, presubmit.OutputApi)) |
628 | 632 |
629 change = self.MakeBasicChange('foo', | 633 change = self.MakeBasicChange('foo', |
630 'Foo\nNEVERTESTED=did some stuff') | 634 'Foo\nNEVERTESTED=did some stuff') |
631 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 635 api = presubmit.InputApi(change, './PRESUBMIT.py') |
632 self.failUnless(presubmit_canned_checks.CheckChangeHasTestedField( | 636 self.failUnless(presubmit_canned_checks.CheckChangeHasTestedField( |
633 api, presubmit.OutputApi)) | 637 api, presubmit.OutputApi)) |
634 | 638 |
635 def testCannedCheckChangeHasQAField(self): | 639 def testCannedCheckChangeHasQAField(self): |
636 change = self.MakeBasicChange('foo', | 640 change = self.MakeBasicChange('foo', |
637 'Foo\nQA=test floop feature very well') | 641 'Foo\nQA=test floop feature very well') |
638 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 642 api = presubmit.InputApi(change, './PRESUBMIT.py') |
639 self.failIf(presubmit_canned_checks.CheckChangeHasQaField( | 643 self.failIf(presubmit_canned_checks.CheckChangeHasQaField( |
640 api, presubmit.OutputApi)) | 644 api, presubmit.OutputApi)) |
641 | 645 |
642 change = self.MakeBasicChange('foo', | 646 change = self.MakeBasicChange('foo', |
643 'Foo\nNOTFORQA=test floop feature very well') | 647 'Foo\nNOTFORQA=test floop feature very well') |
644 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 648 api = presubmit.InputApi(change, './PRESUBMIT.py') |
645 self.failUnless(presubmit_canned_checks.CheckChangeHasQaField( | 649 self.failUnless(presubmit_canned_checks.CheckChangeHasQaField( |
646 api, presubmit.OutputApi)) | 650 api, presubmit.OutputApi)) |
647 | 651 |
648 def testCannedCheckDoNotSubmitInDescription(self): | 652 def testCannedCheckDoNotSubmitInDescription(self): |
649 change = self.MakeBasicChange('foo', 'hello') | 653 change = self.MakeBasicChange('foo', 'hello') |
650 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 654 api = presubmit.InputApi(change, './PRESUBMIT.py') |
651 self.failIf(presubmit_canned_checks.CheckDoNotSubmitInDescription( | 655 self.failIf(presubmit_canned_checks.CheckDoNotSubmitInDescription( |
652 api, presubmit.OutputApi)) | 656 api, presubmit.OutputApi)) |
653 | 657 |
654 change = self.MakeBasicChange('foo', | 658 change = self.MakeBasicChange('foo', |
655 'DO NOT ' + 'SUBMIT') | 659 'DO NOT ' + 'SUBMIT') |
656 api = presubmit.InputApi(change, 'PRESUBMIT.py') | 660 api = presubmit.InputApi(change, './PRESUBMIT.py') |
657 self.failUnless(presubmit_canned_checks.CheckDoNotSubmitInDescription( | 661 self.failUnless(presubmit_canned_checks.CheckDoNotSubmitInDescription( |
658 api, presubmit.OutputApi)) | 662 api, presubmit.OutputApi)) |
659 | 663 |
660 def testCannedCheckDoNotSubmitInFiles(self): | 664 def testCannedCheckDoNotSubmitInFiles(self): |
661 self.failIf(presubmit_canned_checks.CheckDoNotSubmitInFiles( | 665 self.failIf(presubmit_canned_checks.CheckDoNotSubmitInFiles( |
662 self.MockInputApi(['hello', 'there']), presubmit.OutputApi | 666 self.MockInputApi(['hello', 'there']), presubmit.OutputApi |
663 )) | 667 )) |
664 self.failUnless(presubmit_canned_checks.CheckDoNotSubmitInFiles( | 668 self.failUnless(presubmit_canned_checks.CheckDoNotSubmitInFiles( |
665 self.MockInputApi(['hello', 'yo, DO NOT ' + 'SUBMIT']), | 669 self.MockInputApi(['hello', 'yo, DO NOT ' + 'SUBMIT']), |
666 presubmit.OutputApi)) | 670 presubmit.OutputApi)) |
(...skipping 18 matching lines...) Expand all Loading... |
685 self.failIf(presubmit_canned_checks.CheckTreeIsOpen( | 689 self.failIf(presubmit_canned_checks.CheckTreeIsOpen( |
686 self.MockInputApi(), presubmit.OutputApi, url='url_to_open', closed='0' | 690 self.MockInputApi(), presubmit.OutputApi, url='url_to_open', closed='0' |
687 )) | 691 )) |
688 self.failUnless(presubmit_canned_checks.CheckTreeIsOpen( | 692 self.failUnless(presubmit_canned_checks.CheckTreeIsOpen( |
689 self.MockInputApi(), presubmit.OutputApi, url='url_to_closed', closed='0' | 693 self.MockInputApi(), presubmit.OutputApi, url='url_to_closed', closed='0' |
690 )) | 694 )) |
691 | 695 |
692 | 696 |
693 if __name__ == '__main__': | 697 if __name__ == '__main__': |
694 unittest.main() | 698 unittest.main() |
OLD | NEW |