4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35 sys.stdout = StringIO.StringIO()
37 if sys.platform == 'win32':
38 existing_lib = "msvcrt"
42 class SConfTestCase(unittest.TestCase):
45 # we always want to start with a clean directory
46 self.save_cwd = os.getcwd()
47 self.test = TestCmd.TestCmd(workdir = '')
48 os.chdir(self.test.workpath(''))
53 SCons.SConsign.Reset()
54 os.chdir(self.save_cwd)
56 def _resetSConfState(self):
57 # Ok, this is tricky, and i do not know, if everything is sane.
58 # We try to reset scons' state (including all global variables)
60 SCons.SConsign.write() # simulate normal scons-finish
61 for n in sys.modules.keys():
62 if n.split('.')[0] == 'SCons' and n[:12] != 'SCons.compat':
64 if isinstance(m, ModuleType):
65 # if this is really a scons module, clear its namespace
68 # we only use SCons.Environment and SCons.SConf for these tests.
69 import SCons.Environment
71 self.Environment = SCons.Environment
72 self.SConf = SCons.SConf
73 # and we need a new environment, cause references may point to
74 # old modules (well, at least this is safe ...)
75 self.scons_env = self.Environment.Environment()
76 self.scons_env.AppendENVPath('PATH', os.environ['PATH'])
78 # we want to do some autodetection here
79 # this stuff works with
80 # - cygwin on Windows (using cmd.exe, not bash)
82 # - msvc on Windows (hopefully)
83 if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
84 not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
85 not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
86 raise Exception, "This test needs an installed compiler!"
87 if self.scons_env['CXX'] == 'g++':
91 if sys.platform in ['cygwin', 'win32']:
92 # On Windows, SCons.Platform.win32 redefines the builtin
93 # file() and open() functions to close the file handles.
94 # This interferes with the unittest.py infrastructure in
95 # some way. Just sidestep the issue by restoring the
96 # original builtin functions whenever we have to reset
97 # all of our global state.
100 import SCons.Platform.win32
102 __builtin__.file = SCons.Platform.win32._builtin_file
103 __builtin__.open = SCons.Platform.win32._builtin_open
105 def _baseTryXXX(self, TryFunc):
106 # TryCompile and TryLink are much the same, so we can test them
107 # in one method, we pass the function as a string ('TryCompile',
108 # 'TryLink'), so we are aware of reloading modules.
110 def checks(self, sconf, TryFuncString):
111 TryFunc = self.SConf.SConfBase.__dict__[TryFuncString]
112 res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" )
113 res2 = TryFunc( sconf,
114 '#include "no_std_header.h"\nint main() {return 0; }\n',
118 # 1. test initial behaviour (check ok / failed)
119 self._resetSConfState()
120 sconf = self.SConf.SConf(self.scons_env,
121 conf_dir=self.test.workpath('config.tests'),
122 log_file=self.test.workpath('config.log'))
124 res = checks( self, sconf, TryFunc )
125 assert res[0] and not res[1], res
129 # 2.1 test the error caching mechanism (no dependencies have changed)
130 self._resetSConfState()
131 sconf = self.SConf.SConf(self.scons_env,
132 conf_dir=self.test.workpath('config.tests'),
133 log_file=self.test.workpath('config.log'))
135 res = checks( self, sconf, TryFunc )
136 assert res[0] and not res[1], res
139 # we should have exactly one one error cached
140 log = self.test.read( self.test.workpath('config.log') )
141 expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
142 firstOcc = expr.match( log )
143 assert firstOcc is not None, log
144 secondOcc = expr.match( log, firstOcc.end(0) )
145 assert secondOcc is None, log
147 # 2.2 test the error caching mechanism (dependencies have changed)
148 self._resetSConfState()
149 sconf = self.SConf.SConf(self.scons_env,
150 conf_dir=self.test.workpath('config.tests'),
151 log_file=self.test.workpath('config.log'))
152 no_std_header_h = self.test.workpath('config.tests', 'no_std_header.h')
153 test_h = self.test.write( no_std_header_h,
154 "/* we are changing a dependency now */\n" );
156 res = checks( self, sconf, TryFunc )
157 log = self.test.read( self.test.workpath('config.log') )
158 assert res[0] and res[1], res
162 def test_TryBuild(self):
163 """Test SConf.TryBuild
165 # 1 test that we can try a builder that returns a list of nodes
166 self._resetSConfState()
167 sconf = self.SConf.SConf(self.scons_env,
168 conf_dir=self.test.workpath('config.tests'),
169 log_file=self.test.workpath('config.log'))
171 class MyBuilder(SCons.Builder.BuilderBase):
175 def __call__(self, env, target, source):
177 def __init__(self, name):
180 self.waiting_parents = set()
181 self.side_effects = []
183 self.prerequisites = []
184 def disambiguate(self):
186 def has_builder(self):
188 def add_pre_action(self, *actions):
190 def add_post_action(self, *actions):
196 def set_state(self, state):
198 def alter_targets(self):
200 def depends_on(self, nodes):
202 def postprocess(self):
206 def is_up_to_date(self):
210 def push_to_cache(self):
212 def retrieve_from_cache(self):
214 def build(self, **kw):
218 def get_stored_info(self):
220 def do_not_store_info(self):
222 def get_executor(self):
224 def __init__(self, targets):
225 self.targets = targets
226 def get_all_targets(self):
228 return Executor([self])
229 return [MyNode('n1'), MyNode('n2')]
231 self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
232 sconf.TryBuild(self.scons_env.SConfActionBuilder)
236 def test_TryCompile(self):
237 """Test SConf.TryCompile
239 self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
241 def test_TryLink(self):
242 """Test SConf.TryLink
244 self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink )
246 def test_TryRun(self):
257 res1 = sconf.TryRun( prog, ".c" )
258 res2 = sconf.TryRun( "not a c program\n", ".c" )
261 self._resetSConfState()
262 sconf = self.SConf.SConf(self.scons_env,
263 conf_dir=self.test.workpath('config.tests'),
264 log_file=self.test.workpath('config.log'))
267 assert res[0][0] and res[0][1] == "Hello", res
268 assert not res[1][0] and res[1][1] == "", res
271 log = self.test.read( self.test.workpath('config.log') )
273 # test the caching mechanism
274 self._resetSConfState()
275 sconf = self.SConf.SConf(self.scons_env,
276 conf_dir=self.test.workpath('config.tests'),
277 log_file=self.test.workpath('config.log'))
280 assert res[0][0] and res[0][1] == "Hello", res
281 assert not res[1][0] and res[1][1] == "", res
284 # we should have exactly one error cached
285 log = self.test.read( self.test.workpath('config.log') )
286 expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
287 firstOcc = expr.match( log )
288 assert firstOcc is not None, log
289 secondOcc = expr.match( log, firstOcc.end(0) )
290 assert secondOcc is None, log
293 def test_TryAction(self):
294 """Test SConf.TryAction
296 def actionOK(target, source, env):
297 open(str(target[0]), "w").write( "RUN OK\n" )
299 def actionFAIL(target, source, env):
301 self._resetSConfState()
302 sconf = self.SConf.SConf(self.scons_env,
303 conf_dir=self.test.workpath('config.tests'),
304 log_file=self.test.workpath('config.log'))
306 (ret, output) = sconf.TryAction(action=actionOK)
307 assert ret and output == "RUN OK" + os.linesep, (ret, output)
308 (ret, output) = sconf.TryAction(action=actionFAIL)
309 assert not ret and output == "", (ret, output)
313 def _test_check_compilers(self, comp, func, name):
314 """This is the implementation for CheckCC and CheckCXX tests."""
315 from copy import deepcopy
317 # Check that Check* works
319 assert r, "could not find %s ?" % comp
321 # Check that Check* does fail if comp is not available in env
322 oldcomp = deepcopy(self.scons_env[comp])
323 del self.scons_env[comp]
325 assert not r, "%s worked wo comp ?" % name
327 # Check that Check* does fail if comp is set but empty
328 self.scons_env[comp] = ''
330 assert not r, "%s worked with comp = '' ?" % name
332 # Check that Check* does fail if comp is set to buggy executable
333 self.scons_env[comp] = 'thiscccompilerdoesnotexist'
335 assert not r, "%s worked with comp = thiscompilerdoesnotexist ?" % name
337 # Check that Check* does fail if CFLAGS is buggy
338 self.scons_env[comp] = oldcomp
339 self.scons_env['%sFLAGS' % comp] = '/WX qwertyuiop.c'
341 assert not r, "%s worked with %sFLAGS = qwertyuiop ?" % (name, comp)
343 def test_CheckCC(self):
344 """Test SConf.CheckCC()
346 self._resetSConfState()
347 sconf = self.SConf.SConf(self.scons_env,
348 conf_dir=self.test.workpath('config.tests'),
349 log_file=self.test.workpath('config.log'))
352 self._test_check_compilers('CC', sconf.CheckCC, 'CheckCC')
353 except AssertionError:
354 sys.stderr.write(self.test.read('config.log'))
359 def test_CheckSHCC(self):
360 """Test SConf.CheckSHCC()
362 self._resetSConfState()
363 sconf = self.SConf.SConf(self.scons_env,
364 conf_dir=self.test.workpath('config.tests'),
365 log_file=self.test.workpath('config.log'))
368 self._test_check_compilers('SHCC', sconf.CheckSHCC, 'CheckSHCC')
369 except AssertionError:
370 sys.stderr.write(self.test.read('config.log'))
375 def test_CheckCXX(self):
376 """Test SConf.CheckCXX()
378 self._resetSConfState()
379 sconf = self.SConf.SConf(self.scons_env,
380 conf_dir=self.test.workpath('config.tests'),
381 log_file=self.test.workpath('config.log'))
384 self._test_check_compilers('CXX', sconf.CheckCXX, 'CheckCXX')
385 except AssertionError:
386 sys.stderr.write(self.test.read('config.log'))
391 def test_CheckSHCXX(self):
392 """Test SConf.CheckSHCXX()
394 self._resetSConfState()
395 sconf = self.SConf.SConf(self.scons_env,
396 conf_dir=self.test.workpath('config.tests'),
397 log_file=self.test.workpath('config.log'))
400 self._test_check_compilers('SHCXX', sconf.CheckSHCXX, 'CheckSHCXX')
401 except AssertionError:
402 sys.stderr.write(self.test.read('config.log'))
408 def test_CheckHeader(self):
409 """Test SConf.CheckHeader()
411 self._resetSConfState()
412 sconf = self.SConf.SConf(self.scons_env,
413 conf_dir=self.test.workpath('config.tests'),
414 log_file=self.test.workpath('config.log'))
417 r = sconf.CheckHeader( "stdio.h", include_quotes="<>", language="C" )
418 assert r, "did not find stdio.h"
419 r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C" )
420 assert not r, "unexpectedly found HopefullyNoHeader.noh"
421 r = sconf.CheckHeader( "vector", include_quotes="<>", language="C++" )
422 assert r, "did not find vector"
423 r = sconf.CheckHeader( "HopefullyNoHeader.noh", language="C++" )
424 assert not r, "unexpectedly found HopefullyNoHeader.noh"
429 def test_CheckCHeader(self):
430 """Test SConf.CheckCHeader()
432 self._resetSConfState()
433 sconf = self.SConf.SConf(self.scons_env,
434 conf_dir=self.test.workpath('config.tests'),
435 log_file=self.test.workpath('config.log'))
439 r = sconf.CheckCHeader( "stdio.h", include_quotes="<>" )
440 assert r, "did not find stdio.h"
441 r = sconf.CheckCHeader( ["math.h", "stdio.h"], include_quotes="<>" )
442 assert r, "did not find stdio.h, #include math.h first"
443 r = sconf.CheckCHeader( "HopefullyNoCHeader.noh" )
444 assert not r, "unexpectedly found HopefullyNoCHeader.noh"
449 def test_CheckCXXHeader(self):
450 """Test SConf.CheckCXXHeader()
452 self._resetSConfState()
453 sconf = self.SConf.SConf(self.scons_env,
454 conf_dir=self.test.workpath('config.tests'),
455 log_file=self.test.workpath('config.log'))
459 r = sconf.CheckCXXHeader( "vector", include_quotes="<>" )
460 assert r, "did not find vector"
461 r = sconf.CheckCXXHeader( ["stdio.h", "vector"], include_quotes="<>" )
462 assert r, "did not find vector, #include stdio.h first"
463 r = sconf.CheckCXXHeader( "HopefullyNoCXXHeader.noh" )
464 assert not r, "unexpectedly found HopefullyNoCXXHeader.noh"
469 def test_CheckLib(self):
470 """Test SConf.CheckLib()
472 self._resetSConfState()
473 sconf = self.SConf.SConf(self.scons_env,
474 conf_dir=self.test.workpath('config.tests'),
475 log_file=self.test.workpath('config.log'))
479 r = sconf.CheckLib( existing_lib, "main", autoadd=0 )
480 assert r, "did not find %s" % existing_lib
481 r = sconf.CheckLib( "hopefullynolib", "main", autoadd=0 )
482 assert not r, "unexpectedly found hopefullynolib"
484 # CheckLib() with list of libs
485 r = sconf.CheckLib( [existing_lib], "main", autoadd=0 )
486 assert r, "did not find %s" % existing_lib
487 r = sconf.CheckLib( ["hopefullynolib"], "main", autoadd=0 )
488 assert not r, "unexpectedly found hopefullynolib"
489 # This is a check that a null list doesn't find functions
490 # that are in libraries that must be explicitly named.
491 # This works on POSIX systems where you have to -lm to
492 # get the math functions, but it fails on Visual Studio
493 # where you apparently get all those functions for free.
494 # Comment out this check until someone who understands
495 # Visual Studio better can come up with a corresponding
496 # test (if that ever really becomes necessary).
497 #r = sconf.CheckLib( [], "sin", autoadd=0 )
498 #assert not r, "unexpectedly found nonexistent library"
499 r = sconf.CheckLib( [existing_lib,"hopefullynolib"], "main", autoadd=0 )
500 assert r, "did not find %s,%s " % (existing_lib,r)
501 r = sconf.CheckLib( ["hopefullynolib",existing_lib], "main", autoadd=0 )
502 assert r, "did not find %s " % existing_lib
504 # CheckLib() with autoadd
506 return env.get('LIBS', [])
508 env = sconf.env.Clone()
511 r = sconf.CheckLib( existing_lib, "main", autoadd=1 )
512 assert r, "did not find main in %s" % existing_lib
513 expect = libs(env) + [existing_lib]
514 got = libs(sconf.env)
515 assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
517 sconf.env = env.Clone()
518 r = sconf.CheckLib( existing_lib, "main", autoadd=0 )
519 assert r, "did not find main in %s" % existing_lib
521 got = libs(sconf.env)
522 assert got == expect, "before and after LIBS were not the same"
528 def test_CheckLibWithHeader(self):
529 """Test SConf.CheckLibWithHeader()
531 self._resetSConfState()
532 sconf = self.SConf.SConf(self.scons_env,
533 conf_dir=self.test.workpath('config.tests'),
534 log_file=self.test.workpath('config.log'))
537 # CheckLibWithHeader()
538 r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
539 assert r, "did not find %s" % existing_lib
540 r = sconf.CheckLibWithHeader( existing_lib, ["stdio.h", "math.h"], "C", autoadd=0 )
541 assert r, "did not find %s, #include stdio.h first" % existing_lib
542 r = sconf.CheckLibWithHeader( "hopefullynolib", "math.h", "C", autoadd=0 )
543 assert not r, "unexpectedly found hopefullynolib"
545 # CheckLibWithHeader() with lists of libs
546 r = sconf.CheckLibWithHeader( [existing_lib], "math.h", "C", autoadd=0 )
547 assert r, "did not find %s" % existing_lib
548 r = sconf.CheckLibWithHeader( [existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 )
549 assert r, "did not find %s, #include stdio.h first" % existing_lib
550 # This is a check that a null list doesn't find functions
551 # that are in libraries that must be explicitly named.
552 # This works on POSIX systems where you have to -lm to
553 # get the math functions, but it fails on Visual Studio
554 # where you apparently get all those functions for free.
555 # Comment out this check until someone who understands
556 # Visual Studio better can come up with a corresponding
557 # test (if that ever really becomes necessary).
558 #r = sconf.CheckLibWithHeader( [], "math.h", "C", call="sin(3);", autoadd=0 )
559 #assert not r, "unexpectedly found non-existent library"
560 r = sconf.CheckLibWithHeader( ["hopefullynolib"], "math.h", "C", autoadd=0 )
561 assert not r, "unexpectedly found hopefullynolib"
562 r = sconf.CheckLibWithHeader( ["hopefullynolib",existing_lib], ["stdio.h", "math.h"], "C", autoadd=0 )
563 assert r, "did not find %s, #include stdio.h first" % existing_lib
564 r = sconf.CheckLibWithHeader( [existing_lib,"hopefullynolib"], ["stdio.h", "math.h"], "C", autoadd=0 )
565 assert r, "did not find %s, #include stdio.h first" % existing_lib
567 # CheckLibWithHeader with autoadd
569 return env.get('LIBS', [])
571 env = sconf.env.Clone()
574 r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=1 )
575 assert r, "did not find math.h with %s" % existing_lib
576 expect = libs(env) + [existing_lib]
577 got = libs(sconf.env)
578 assert got == expect, "LIBS: expected %s, got %s" % (expect, got)
580 sconf.env = env.Clone()
581 r = sconf.CheckLibWithHeader( existing_lib, "math.h", "C", autoadd=0 )
582 assert r, "did not find math.h with %s" % existing_lib
584 got = libs(sconf.env)
585 assert got == expect, "before and after LIBS were not the same"
592 def test_CheckFunc(self):
593 """Test SConf.CheckFunc()
595 self._resetSConfState()
596 sconf = self.SConf.SConf(self.scons_env,
597 conf_dir=self.test.workpath('config.tests'),
598 log_file=self.test.workpath('config.log'))
602 r = sconf.CheckFunc('strcpy')
603 assert r, "did not find strcpy"
604 r = sconf.CheckFunc('strcpy', '/* header */ char strcpy();')
605 assert r, "did not find strcpy"
606 r = sconf.CheckFunc('hopefullynofunction')
607 assert not r, "unexpectedly found hopefullynofunction"
612 def test_Define(self):
613 """Test SConf.Define()
615 self._resetSConfState()
616 sconf = self.SConf.SConf(self.scons_env,
617 conf_dir=self.test.workpath('config.tests'),
618 log_file=self.test.workpath('config.log'),
619 config_h = self.test.workpath('config.h'))
621 # XXX: we test the generated config.h string. This is not so good,
622 # ideally, we would like to test if the generated file included in
623 # a test program does what we want.
625 # Test defining one symbol wo value
626 sconf.config_h_text = ''
628 assert sconf.config_h_text == '#define YOP\n'
630 # Test defining one symbol with integer value
631 sconf.config_h_text = ''
632 sconf.Define('YOP', 1)
633 assert sconf.config_h_text == '#define YOP 1\n'
635 # Test defining one symbol with string value
636 sconf.config_h_text = ''
637 sconf.Define('YOP', '"YIP"')
638 assert sconf.config_h_text == '#define YOP "YIP"\n'
640 # Test defining one symbol with string value
641 sconf.config_h_text = ''
642 sconf.Define('YOP', "YIP")
643 assert sconf.config_h_text == '#define YOP YIP\n'
648 def test_CheckTypeSize(self):
649 """Test SConf.CheckTypeSize()
651 self._resetSConfState()
652 sconf = self.SConf.SConf(self.scons_env,
653 conf_dir=self.test.workpath('config.tests'),
654 log_file=self.test.workpath('config.log'))
658 # In ANSI C, sizeof(char) == 1.
659 r = sconf.CheckTypeSize('char', expect = 1)
660 assert r == 1, "sizeof(char) != 1 ??"
661 r = sconf.CheckTypeSize('char', expect = 0)
662 assert r == 0, "sizeof(char) == 0 ??"
663 r = sconf.CheckTypeSize('char', expect = 2)
664 assert r == 0, "sizeof(char) == 2 ??"
665 r = sconf.CheckTypeSize('char')
666 assert r == 1, "sizeof(char) != 1 ??"
667 r = sconf.CheckTypeSize('const unsigned char')
668 assert r == 1, "sizeof(const unsigned char) != 1 ??"
671 r = sconf.CheckTypeSize('const unsigned char', language = 'C++')
672 assert r == 1, "sizeof(const unsigned char) != 1 ??"
674 # Checking Non-existing type
675 r = sconf.CheckTypeSize('thistypedefhasnotchancetosexist_scons')
677 "Checking size of thistypedefhasnotchancetosexist_scons succeeded ?"
682 def test_CheckDeclaration(self):
683 """Test SConf.CheckDeclaration()
685 self._resetSConfState()
686 sconf = self.SConf.SConf(self.scons_env,
687 conf_dir=self.test.workpath('config.tests'),
688 log_file=self.test.workpath('config.log'))
690 # In ANSI C, malloc should be available in stdlib
691 r = sconf.CheckDeclaration('malloc', includes = "#include <stdlib.h>")
692 assert r, "malloc not declared ??"
693 # For C++, __cplusplus should be declared
694 r = sconf.CheckDeclaration('__cplusplus', language = 'C++')
695 assert r, "__cplusplus not declared in C++ ??"
696 r = sconf.CheckDeclaration('__cplusplus', language = 'C')
697 assert not r, "__cplusplus declared in C ??"
702 """Test SConf.CheckType()
704 self._resetSConfState()
705 sconf = self.SConf.SConf(self.scons_env,
706 conf_dir=self.test.workpath('config.tests'),
707 log_file=self.test.workpath('config.log'))
710 r = sconf.CheckType('off_t', '#include <sys/types.h>\n')
711 assert r, "did not find off_t"
712 r = sconf.CheckType('hopefullynotypedef_not')
713 assert not r, "unexpectedly found hopefullynotypedef_not"
718 def test_CustomChecks(self):
719 """Test Custom Checks
721 def CheckCustom(test):
722 test.Message( "Checking UserTest ... " )
731 (ret, output) = test.TryRun( prog, ".c" )
733 assert ret and output == "Hello", (ret, output)
737 self._resetSConfState()
738 sconf = self.SConf.SConf(self.scons_env,
739 custom_tests={'CheckCustom': CheckCustom},
740 conf_dir=self.test.workpath('config.tests'),
741 log_file=self.test.workpath('config.log'))
743 ret = sconf.CheckCustom()
749 if __name__ == "__main__":
750 suite = unittest.makeSuite(SConfTestCase, 'test_')
751 res = unittest.TextTestRunner().run(suite)
752 if not res.wasSuccessful():
757 # indent-tabs-mode:nil
759 # vim: set expandtab tabstop=4 shiftwidth=4: