Browse Source

added task profiler

Darren Ranalli 17 years ago
parent
commit
6895e25221
2 changed files with 688 additions and 500 deletions
  1. 88 9
      direct/src/showbase/PythonUtil.py
  2. 600 491
      direct/src/task/Task.py

+ 88 - 9
direct/src/showbase/PythonUtil.py

@@ -30,7 +30,8 @@ __all__ = ['enumerate', 'unique', 'indent', 'nonRepeatingRandomList',
 'nullGen', 'loopGen', 'makeFlywheelGen', 'flywheel', 'choice',
 'printStack', 'printReverseStack', 'listToIndex2item', 'listToItem2index',
 'pandaBreak','pandaTrace','formatTimeCompact','DestructiveScratchPad',
-'deeptype',]
+'deeptype','getProfileResultString','StdoutCapture','StdoutPassthrough',
+'Averager',]
 
 import types
 import string
@@ -794,12 +795,47 @@ def binaryRepr(number, max_length = 32):
     digits = digits [digits.index (1):]
     return string.join (map (repr, digits), '')
 
+class StdoutCapture:
+    # redirects stdout to a string
+    def __init__(self):
+        self._oldStdout = sys.stdout
+        sys.stdout = self
+        self._string = ''
+    def destroy(self):
+        sys.stdout = self._oldStdout
+        del self._oldStdout
+
+    def getString(self):
+        return self._string
+
+    # internal
+    def write(self, string):
+        self._string = ''.join([self._string, string])
+
+class StdoutPassthrough(StdoutCapture):
+    # like StdoutCapture but also allows output to go through to the OS as normal
+
+    # internal
+    def write(self, string):
+        self._string = ''.join([self._string, string])
+        self._oldStdout.write(string)
+
 # constant profile defaults
 PyUtilProfileDefaultFilename = 'profiledata'
 PyUtilProfileDefaultLines = 80
 PyUtilProfileDefaultSorts = ['cumulative', 'time', 'calls']
 
-def profile(callback, name, terse):
+_ProfileResultStr = ''
+
+def getProfileResultString():
+    # if you called profile with 'log' not set to True,
+    # you can call this function to get the results as
+    # a string
+    global _ProfileResultStr
+    return _ProfileResultStr
+
+def profile(callback, name, terse, log=True):
+    global _ProfileResultStr
     import __builtin__
     if 'globalProfileFunc' in __builtin__.__dict__:
         # rats. Python profiler is not re-entrant...
@@ -811,10 +847,20 @@ def profile(callback, name, terse):
             ))
         return
     __builtin__.globalProfileFunc = callback
-    print '***** START PROFILE: %s *****' % name
-    startProfile(cmd='globalProfileFunc()', callInfo=(not terse))
-    print '***** END PROFILE: %s *****' % name
+    __builtin__.globalProfileResult = [None]
+    prefix = '***** START PROFILE: %s *****' % name
+    if log:
+        print prefix
+    startProfile(cmd='globalProfileResult[0]=globalProfileFunc()', callInfo=(not terse), silent=not log)
+    suffix = '***** END PROFILE: %s *****' % name
+    if log:
+        print suffix
+    else:
+        _ProfileResultStr = '%s\n%s\n%s' % (prefix, _ProfileResultStr, suffix)
+    result = globalProfileResult[0]
     del __builtin__.__dict__['globalProfileFunc']
+    del __builtin__.__dict__['globalProfileResult']
+    return result
 
 def profiled(category=None, terse=False):
     """ decorator for profiling functions
@@ -883,12 +929,14 @@ def startProfile(filename=PyUtilProfileDefaultFilename,
     filename = '%s.%s' % (filename, randUint31())
     import profile
     profile.run(cmd, filename)
-    if not silent:
+    if silent:
+        extractProfile(filename, lines, sorts, callInfo)
+    else:
         printProfile(filename, lines, sorts, callInfo)
     import os
     os.remove(filename)
 
-# call this to see the results again
+# call these to see the results again, as a string or in the log
 def printProfile(filename=PyUtilProfileDefaultFilename,
                  lines=PyUtilProfileDefaultLines,
                  sorts=PyUtilProfileDefaultSorts,
@@ -903,6 +951,18 @@ def printProfile(filename=PyUtilProfileDefaultFilename,
             s.print_callees(lines)
             s.print_callers(lines)
 
+# same args as printProfile
+def extractProfile(*args, **kArgs):
+    global _ProfileResultStr
+    # capture print output
+    sc = StdoutCapture()
+    # print the profile output, redirected to the result string
+    printProfile(*args, **kArgs)
+    # make a copy of the print output
+    _ProfileResultStr = sc.getString()
+    # restore stdout to what it was before
+    sc.destroy()
+
 def getSetterName(valueName, prefix='set'):
     # getSetterName('color') -> 'setColor'
     # getSetterName('color', 'get') -> 'getColor'
@@ -927,8 +987,14 @@ class Functor:
         self._function = function
         self._args = args
         self._kargs = kargs
-        self.__name__ = self._function.__name__
-        self.__doc__ = self._function.__doc__
+        if hasattr(self._function, '__name__'):
+            self.__name__ = self._function.__name__
+        else:
+            self.__name__ = str(itype(self._function))
+        if hasattr(self._function, '__doc__'):
+            self.__doc__ = self._function.__doc__
+        else:
+            self.__doc__ = self.__name__
 
     def destroy(self):
         del self._function
@@ -1714,6 +1780,19 @@ def average(*args):
         val += arg
     return val / len(args)
 
+class Averager:
+    def __init__(self, name):
+        self._name = name
+        self._total = 0.
+        self._count = 0
+    def addValue(self, value):
+        self._total += value
+        self._count += 1
+    def getAverage(self):
+        return self._total / self._count
+    def getCount(self):
+        return self._count
+
 def addListsByValue(a, b):
     """
     returns a new array containing the sums of the two array arguments

+ 600 - 491
direct/src/task/Task.py

@@ -19,6 +19,7 @@ import time
 import fnmatch
 import string
 import signal
+import random
 try:
     Dtool_PreloadDLL("libp3heapq")
     from libp3heapq import heappush, heappop, heapify
@@ -161,6 +162,13 @@ class Task:
         self.time = currentTime - self.starttime
         self.frame = currentFrame - self.startframe
 
+    def getNamePattern(self, taskName=None):
+        # get a version of the task name that doesn't contain any numbers
+        digits = '0123456789'
+        if taskName is None:
+            taskName = self.name
+        return ''.join([c for c in taskName if c not in digits])
+
     def setupPStats(self):
         if __debug__ and TaskManager.taskTimerVerbose and not self.pstats:
             # Get the PStats name for the task.  By convention,
@@ -358,6 +366,8 @@ class TaskManager:
     # multiple of average frame duration
     DefTaskDurationWarningThreshold = 40.
 
+    _DidTests = False
+
     def __init__(self):
         self.running = 0
         self.stepping = 0
@@ -370,6 +380,12 @@ class TaskManager:
         self._profileFrames = False
         self.MaxEpockSpeed = 1.0/30.0;   
 
+        # this will be set when it's safe to import StateVar
+        self._profileTasks = None
+        self._taskProfiler = None
+        self._profileTaskId = None
+        self._profileDt = None
+        self._lastProfileResultString = None
 
         # We copy this value in from __builtins__ when it gets set.
         # But since the TaskManager might have to run before it gets
@@ -404,6 +420,8 @@ class TaskManager:
         self.add(self.__doLaterProcessor, "doLaterProcessor", -10)
 
     def destroy(self):
+        if self._taskProfiler:
+            self._taskProfiler.destroy()
         del self.nameDict
         del self.trueClock
         del self.globalClock
@@ -722,15 +740,27 @@ class TaskManager:
 
     def __executeTask(self, task):
         task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
+
+        doProfile = (task.id == self._profileTaskId)
+            
         if not self.taskTimerVerbose:
             startTime = self.trueClock.getShortTime()
             
             # don't record timing info
-            ret = task(*task.extraArgs)
+            if doProfile:
+                ret = profile(Functor(task, *task.extraArgs),
+                              'TASK_PROFILE:%s' % task.name, True, log=False)
+            else:
+                ret = task(*task.extraArgs)
             endTime = self.trueClock.getShortTime()
             
             # Record the dt
             dt = endTime - startTime
+            if doProfile:
+                # if we profiled, record the measured duration but don't pollute the task's
+                # normal duration
+                self._profileDt = dt
+                dt = task.avgDt
             task.dt = dt
 
         else:
@@ -738,13 +768,22 @@ class TaskManager:
             if task.pstats:
                 task.pstats.start()
             startTime = self.trueClock.getShortTime()
-            ret = task(*task.extraArgs)
+            if doProfile:
+                ret = profile(Functor(task, *task.extraArgs),
+                              'profiled-task-%s' % task.name, True, log=False)
+            else:
+                ret = task(*task.extraArgs)
             endTime = self.trueClock.getShortTime()
             if task.pstats:
                 task.pstats.stop()
 
             # Record the dt
             dt = endTime - startTime
+            if doProfile:
+                # if we profiled, record the measured duration but don't pollute the task's
+                # normal duration
+                self._profileDt = dt
+                dt = task.avgDt
             task.dt = dt
 
             # See if this is the new max
@@ -758,6 +797,9 @@ class TaskManager:
             else:
                 task.avgDt = 0
 
+        if doProfile:
+            self._lastProfileResultString = self._getProfileResultString()
+
         # warn if the task took too long
         if self.warnTaskDuration and self.globalClock:
             avgFrameRate = self.globalClock.getAverageFrameRate()
@@ -889,6 +931,65 @@ class TaskManager:
             result = self.step(*args, **kArgs)
         return result
 
+    def getProfileTasks(self):
+        return self._profileTasks.get()
+
+    def getProfileTasksSV(self):
+        return self._profileTasks
+
+    def setProfileTasks(self, profileTasks):
+        self._profileTasks.set(profileTasks)
+        if (not self._taskProfiler) and profileTasks:
+            # import here due to import dependencies
+            from direct.task.TaskProfiler import TaskProfiler
+            self._taskProfiler = TaskProfiler()
+
+    def _setProfileTask(self, task):
+        self._profileTaskId = task.id
+        self._profileDt = None
+        self._lastProfileResultString = None
+
+    def _getTaskProfileDt(self):
+        return self._profileDt
+
+    def _getLastProfileResultString(self):
+        return self._lastProfileResultString
+
+    def _getRandomTask(self):
+        numTasks = 0
+        for name in self.nameDict.iterkeys():
+            numTasks += len(self.nameDict[name])
+        numDoLaters = len(self.__doLaterList)
+        if random.random() < (numDoLaters / float(numTasks + numDoLaters)):
+            # grab a doLater that will most likely trigger in the next frame
+            tNow = globalClock.getFrameTime()
+            avgFrameRate = globalClock.getAverageFrameRate()
+            if avgFrameRate < .00001:
+                avgFrameDur = 0.
+            else:
+                avgFrameDur = (1. / globalClock.getAverageFrameRate())
+            tNext = tNow + avgFrameDur
+            # binary search to find doLaters that are likely to trigger on the next frame
+            curIndex = int(numDoLaters / 2)
+            rangeStart = 0
+            rangeEnd = numDoLaters
+            while True:
+                if tNext < self.__doLaterList[curIndex].wakeTime:
+                    rangeEnd = curIndex
+                else:
+                    rangeStart = curIndex
+                prevIndex = curIndex
+                curIndex = int((rangeStart + rangeEnd) / 2)
+                if curIndex == prevIndex:
+                    break
+            index = curIndex
+            task = self.__doLaterList[random.randrange(index+1)]
+        else:
+            # grab a task
+            name = random.choice(self.nameDict.keys())
+            task = random.choice(self.nameDict[name])
+        return task
+
     def step(self):
         # assert TaskManager.notify.debug('step: begin')
         self.currentTime, self.currentFrame = self.__getTimeFrame()
@@ -952,6 +1053,16 @@ class TaskManager:
 
 
     def run(self):
+        # do things that couldn't be done earlier because of import dependencies
+        if (not TaskManager._DidTests) and __debug__:
+            TaskManager._DidTests = True
+            self._runTests()
+
+        if not self._profileTasks:
+            from direct.fsm.StatePush import StateVar
+            self._profileTasks = StateVar(False)
+            self.setProfileTasks(getBase().config.GetBool('profile-task-spikes', 0))
+
         # Set the clock to have last frame's time in case we were
         # Paused at the prompt for a long time
         if self.globalClock:
@@ -1279,6 +1390,492 @@ class TaskManager:
             dtfmt % (totalAvgDt*1000),))
         return cont
 
+    def _runTests(self):
+        if __debug__:
+            tm = TaskManager()
+            # looks like nothing runs on the first frame...?
+            # step to get past the first frame
+            tm.step()
+
+            # check for memory leaks after every test
+            tm._startTrackingMemLeaks()
+            tm._checkMemLeaks()
+
+            # run-once task
+            l = []
+            def _testDone(task, l=l):
+                l.append(None)
+                return task.done
+            tm.add(_testDone, 'testDone')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 1
+            _testDone = None
+            tm._checkMemLeaks()
+
+            # remove by name
+            def _testRemoveByName(task):
+                return task.done
+            tm.add(_testRemoveByName, 'testRemoveByName')
+            assert tm.remove('testRemoveByName') == 1
+            assert tm.remove('testRemoveByName') == 0
+            _testRemoveByName = None
+            tm._checkMemLeaks()
+
+            # duplicate named tasks
+            def _testDupNamedTasks(task):
+                return task.done
+            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
+            tm.add(_testDupNamedTasks, 'testDupNamedTasks')
+            assert tm.remove('testRemoveByName') == 0
+            _testDupNamedTasks = None
+            tm._checkMemLeaks()
+
+            # continued task
+            l = []
+            def _testCont(task, l = l):
+                l.append(None)
+                return task.cont
+            tm.add(_testCont, 'testCont')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            tm.remove('testCont')
+            _testCont = None
+            tm._checkMemLeaks()
+
+            # continue until done task
+            l = []
+            def _testContDone(task, l = l):
+                l.append(None)
+                if len(l) >= 2:
+                    return task.done
+                else:
+                    return task.cont
+            tm.add(_testContDone, 'testContDone')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            tm.step()
+            assert len(l) == 2
+            assert not tm.hasTaskNamed('testContDone')
+            _testContDone = None
+            tm._checkMemLeaks()
+
+            # hasTaskNamed
+            def _testHasTaskNamed(task):
+                return task.done
+            tm.add(_testHasTaskNamed, 'testHasTaskNamed')
+            assert tm.hasTaskNamed('testHasTaskNamed')
+            tm.step()
+            assert not tm.hasTaskNamed('testHasTaskNamed')
+            _testHasTaskNamed = None
+            tm._checkMemLeaks()
+
+            # task priority
+            l = []
+            def _testPri1(task, l = l):
+                l.append(1)
+                return task.cont
+            def _testPri2(task, l = l):
+                l.append(2)
+                return task.cont
+            tm.add(_testPri1, 'testPri1', priority = 1)
+            tm.add(_testPri2, 'testPri2', priority = 2)
+            tm.step()
+            assert len(l) == 2
+            assert l == [1, 2,]
+            tm.step()
+            assert len(l) == 4
+            assert l == [1, 2, 1, 2,]
+            tm.remove('testPri1')
+            tm.remove('testPri2')
+            _testPri1 = None
+            _testPri2 = None
+            tm._checkMemLeaks()
+
+            # task extraArgs
+            l = []
+            def _testExtraArgs(arg1, arg2, l=l):
+                l.extend([arg1, arg2,])
+                return done
+            tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
+            tm.step()
+            assert len(l) == 2
+            assert l == [4, 5,]
+            _testExtraArgs = None
+            tm._checkMemLeaks()
+
+            # task appendTask
+            l = []
+            def _testAppendTask(arg1, arg2, task, l=l):
+                l.extend([arg1, arg2,])
+                return task.done
+            tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
+            tm.step()
+            assert len(l) == 2
+            assert l == [4, 5,]
+            _testAppendTask = None
+            tm._checkMemLeaks()
+
+            # task uponDeath
+            l = []
+            def _uponDeathFunc(task, l=l):
+                l.append(task.name)
+            def _testUponDeath(task):
+                return done
+            tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
+            tm.step()
+            assert len(l) == 1
+            assert l == ['testUponDeath']
+            _testUponDeath = None
+            _uponDeathFunc = None
+            tm._checkMemLeaks()
+
+            # task owner
+            class _TaskOwner:
+                def _clearTask(self, task):
+                    self.clearedTaskName = task.name
+            to = _TaskOwner()
+            l = []
+            def _testOwner(task):
+                return done
+            tm.add(_testOwner, 'testOwner', owner=to)
+            tm.step()
+            assert hasattr(to, 'clearedTaskName')
+            assert to.clearedTaskName == 'testOwner'
+            _testOwner = None
+            del to
+            _TaskOwner = None
+            tm._checkMemLeaks()
+
+
+            doLaterTests = [0,]
+
+            # doLater
+            l = []
+            def _testDoLater1(task, l=l):
+                l.append(1)
+            def _testDoLater2(task, l=l):
+                l.append(2)
+            def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .03:
+                    assert l == [1, 2,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
+            tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLater, 'monitorDoLater', priority=10)
+            _testDoLater1 = None
+            _testDoLater2 = None
+            _monitorDoLater = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater priority
+            l = []
+            def _testDoLaterPri1(task, l=l):
+                l.append(1)
+            def _testDoLaterPri2(task, l=l):
+                l.append(2)
+            def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [1, 2,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', priority=1)
+            tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', priority=2)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', priority=10)
+            _testDoLaterPri1 = None
+            _testDoLaterPri2 = None
+            _monitorDoLaterPri = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater extraArgs
+            l = []
+            def _testDoLaterExtraArgs(arg1, l=l):
+                l.append(arg1)
+            def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [3,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', priority=10)
+            _testDoLaterExtraArgs = None
+            _monitorDoLaterExtraArgs = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater appendTask
+            l = []
+            def _testDoLaterAppendTask(arg1, task, l=l):
+                assert task.name == 'testDoLaterAppendTask'
+                l.append(arg1)
+            def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [4,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
+                             extraArgs=[4,], appendTask=True)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', priority=10)
+            _testDoLaterAppendTask = None
+            _monitorDoLaterAppendTask = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater uponDeath
+            l = []
+            def _testUponDeathFunc(task, l=l):
+                assert task.name == 'testDoLaterUponDeath'
+                l.append(10)
+            def _testDoLaterUponDeath(arg1, l=l):
+                return done
+            def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert l == [10,]
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
+                             uponDeath=_testUponDeathFunc)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', priority=10)
+            _testUponDeathFunc = None
+            _testDoLaterUponDeath = None
+            _monitorDoLaterUponDeath = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # doLater owner
+            class _DoLaterOwner:
+                def _clearTask(self, task):
+                    self.clearedTaskName = task.name
+            doLaterOwner = _DoLaterOwner()
+            l = []
+            def _testDoLaterOwner(l=l):
+                pass
+            def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
+                                     doLaterTests=doLaterTests):
+                if task.time > .02:
+                    assert hasattr(doLaterOwner, 'clearedTaskName')
+                    assert doLaterOwner.clearedTaskName == 'testDoLaterOwner'
+                    doLaterTests[0] -= 1
+                    return task.done
+                return task.cont
+            tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
+                             owner=doLaterOwner)
+            doLaterTests[0] += 1
+            # make sure we run this task after the doLaters if they all occur on the same frame
+            tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', priority=10)
+            _testDoLaterOwner = None
+            _monitorDoLaterOwner = None
+            del doLaterOwner
+            _DoLaterOwner = None
+            # don't check until all the doLaters are finished
+            #tm._checkMemLeaks()
+
+            # run the doLater tests
+            while doLaterTests[0] > 0:
+                tm.step()
+            del doLaterTests
+            tm._checkMemLeaks()
+
+            # getTasks
+            def _testGetTasks(task):
+                return task.cont
+            # the doLaterProcessor is always running
+            assert len(tm.getTasks()) == 1
+            tm.add(_testGetTasks, 'testGetTasks1')
+            assert len(tm.getTasks()) == 2
+            assert (tm.getTasks()[0].name == 'testGetTasks1' or
+                    tm.getTasks()[1].name == 'testGetTasks1')
+            tm.add(_testGetTasks, 'testGetTasks2')
+            tm.add(_testGetTasks, 'testGetTasks3')
+            assert len(tm.getTasks()) == 4
+            tm.remove('testGetTasks2')
+            assert len(tm.getTasks()) == 3
+            tm.remove('testGetTasks1')
+            tm.remove('testGetTasks3')
+            assert len(tm.getTasks()) == 1
+            _testGetTasks = None
+            tm._checkMemLeaks()
+
+            # getDoLaters
+            def _testGetDoLaters():
+                pass
+            # the doLaterProcessor is always running
+            assert len(tm.getDoLaters()) == 0
+            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
+            assert len(tm.getDoLaters()) == 1
+            assert tm.getDoLaters()[0].name == 'testDoLater1'
+            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
+            tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
+            assert len(tm.getDoLaters()) == 3
+            tm.remove('testDoLater2')
+            assert len(tm.getDoLaters()) == 2
+            tm.remove('testDoLater1')
+            tm.remove('testDoLater3')
+            assert len(tm.getDoLaters()) == 0
+            _testGetDoLaters = None
+            tm._checkMemLeaks()
+
+            # duplicate named doLaters removed via taskMgr.remove
+            def _testDupNameDoLaters():
+                pass
+            # the doLaterProcessor is always running
+            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
+            tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
+            assert len(tm.getDoLaters()) == 2
+            tm.remove('testDupNameDoLater')
+            assert len(tm.getDoLaters()) == 0
+            _testDupNameDoLaters = None
+            tm._checkMemLeaks()
+
+            # duplicate named doLaters removed via remove()
+            def _testDupNameDoLatersRemove():
+                pass
+            # the doLaterProcessor is always running
+            dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
+            dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
+            assert len(tm.getDoLaters()) == 2
+            dl2.remove()
+            assert len(tm.getDoLaters()) == 1
+            dl1.remove()
+            assert len(tm.getDoLaters()) == 0
+            _testDupNameDoLatersRemove = None
+            # nameDict etc. isn't cleared out right away with task.remove()
+            tm._checkMemLeaks()
+
+            # getTasksNamed
+            def _testGetTasksNamed(task):
+                return task.cont
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
+            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
+            assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
+            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
+            tm.add(_testGetTasksNamed, 'testGetTasksNamed')
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
+            tm.remove('testGetTasksNamed')
+            assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
+            _testGetTasksNamed = None
+            tm._checkMemLeaks()
+
+            # removeTasksMatching
+            def _testRemoveTasksMatching(task):
+                return task.cont
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
+            tm.removeTasksMatching('testRemoveTasksMatching')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
+            tm.removeTasksMatching('testRemoveTasksMatching*')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
+            tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
+            tm.removeTasksMatching('testRemoveTasksMatching?a')
+            assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
+            assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
+            _testRemoveTasksMatching = None
+            tm._checkMemLeaks()
+
+            # create Task object and add to mgr
+            l = []
+            def _testTaskObj(task, l=l):
+                l.append(None)
+                return task.cont
+            t = Task(_testTaskObj)
+            tm.add(t, 'testTaskObj')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            tm.remove('testTaskObj')
+            tm.step()
+            assert len(l) == 2
+            _testTaskObj = None
+            tm._checkMemLeaks()
+
+            # remove Task via task.remove()
+            l = []
+            def _testTaskObjRemove(task, l=l):
+                l.append(None)
+                return task.cont
+            t = Task(_testTaskObjRemove)
+            tm.add(t, 'testTaskObjRemove')
+            tm.step()
+            assert len(l) == 1
+            tm.step()
+            assert len(l) == 2
+            t.remove()
+            tm.step()
+            assert len(l) == 2
+            del t
+            _testTaskObjRemove = None
+            tm._checkMemLeaks()
+
+            """
+            # this test fails, and it's not clear what the correct behavior should be.
+            # priority passed to Task.__init__ is always overridden by taskMgr.add()
+            # even if no priority is specified, and calling Task.setPriority() has no
+            # effect on the taskMgr's behavior.
+            # set/get Task priority
+            l = []
+            def _testTaskObjPriority(arg, task, l=l):
+                l.append(arg)
+                return task.cont
+            t1 = Task(_testTaskObjPriority, priority=1)
+            t2 = Task(_testTaskObjPriority, priority=2)
+            tm.add(t1, 'testTaskObjPriority1', extraArgs=['a',], appendTask=True)
+            tm.add(t2, 'testTaskObjPriority2', extraArgs=['b',], appendTask=True)
+            tm.step()
+            assert len(l) == 2
+            assert l == ['a', 'b']
+            assert t1.getPriority() == 1
+            assert t2.getPriority() == 2
+            t1.setPriority(3)
+            assert t1.getPriority() == 3
+            tm.step()
+            assert len(l) == 4
+            assert l == ['a', 'b', 'b', 'a',]
+            t1.remove()
+            t2.remove()
+            tm.step()
+            assert len(l) == 4
+            del t1
+            del t2
+            _testTaskObjPriority = None
+            tm._checkMemLeaks()
+            """
+
+            del l
+            tm.destroy()
+            del tm
 
 
 # These constants are moved to the top level of the module,
@@ -1292,495 +1889,7 @@ again = Task.again
 
 
 if __debug__:
-    # keep everything in a function namespace so it's easier to clean up
-    def runTests():
-        tm = TaskManager()
-        # looks like nothing runs on the first frame...?
-        # step to get past the first frame
-        tm.step()
-
-        # check for memory leaks after every test
-        tm._startTrackingMemLeaks()
-        tm._checkMemLeaks()
-
-        # run-once task
-        l = []
-        def _testDone(task, l=l):
-            l.append(None)
-            return task.done
-        tm.add(_testDone, 'testDone')
-        tm.step()
-        assert len(l) == 1
-        tm.step()
-        assert len(l) == 1
-        _testDone = None
-        tm._checkMemLeaks()
-
-        # remove by name
-        def _testRemoveByName(task):
-            return task.done
-        tm.add(_testRemoveByName, 'testRemoveByName')
-        assert tm.remove('testRemoveByName') == 1
-        assert tm.remove('testRemoveByName') == 0
-        _testRemoveByName = None
-        tm._checkMemLeaks()
-
-        # duplicate named tasks
-        def _testDupNamedTasks(task):
-            return task.done
-        tm.add(_testDupNamedTasks, 'testDupNamedTasks')
-        tm.add(_testDupNamedTasks, 'testDupNamedTasks')
-        assert tm.remove('testRemoveByName') == 0
-        _testDupNamedTasks = None
-        tm._checkMemLeaks()
-
-        # continued task
-        l = []
-        def _testCont(task, l = l):
-            l.append(None)
-            return task.cont
-        tm.add(_testCont, 'testCont')
-        tm.step()
-        assert len(l) == 1
-        tm.step()
-        assert len(l) == 2
-        tm.remove('testCont')
-        _testCont = None
-        tm._checkMemLeaks()
-
-        # continue until done task
-        l = []
-        def _testContDone(task, l = l):
-            l.append(None)
-            if len(l) >= 2:
-                return task.done
-            else:
-                return task.cont
-        tm.add(_testContDone, 'testContDone')
-        tm.step()
-        assert len(l) == 1
-        tm.step()
-        assert len(l) == 2
-        tm.step()
-        assert len(l) == 2
-        assert not tm.hasTaskNamed('testContDone')
-        _testContDone = None
-        tm._checkMemLeaks()
-
-        # hasTaskNamed
-        def _testHasTaskNamed(task):
-            return task.done
-        tm.add(_testHasTaskNamed, 'testHasTaskNamed')
-        assert tm.hasTaskNamed('testHasTaskNamed')
-        tm.step()
-        assert not tm.hasTaskNamed('testHasTaskNamed')
-        _testHasTaskNamed = None
-        tm._checkMemLeaks()
-
-        # task priority
-        l = []
-        def _testPri1(task, l = l):
-            l.append(1)
-            return task.cont
-        def _testPri2(task, l = l):
-            l.append(2)
-            return task.cont
-        tm.add(_testPri1, 'testPri1', priority = 1)
-        tm.add(_testPri2, 'testPri2', priority = 2)
-        tm.step()
-        assert len(l) == 2
-        assert l == [1, 2,]
-        tm.step()
-        assert len(l) == 4
-        assert l == [1, 2, 1, 2,]
-        tm.remove('testPri1')
-        tm.remove('testPri2')
-        _testPri1 = None
-        _testPri2 = None
-        tm._checkMemLeaks()
-
-        # task extraArgs
-        l = []
-        def _testExtraArgs(arg1, arg2, l=l):
-            l.extend([arg1, arg2,])
-            return done
-        tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
-        tm.step()
-        assert len(l) == 2
-        assert l == [4, 5,]
-        _testExtraArgs = None
-        tm._checkMemLeaks()
-
-        # task appendTask
-        l = []
-        def _testAppendTask(arg1, arg2, task, l=l):
-            l.extend([arg1, arg2,])
-            return task.done
-        tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
-        tm.step()
-        assert len(l) == 2
-        assert l == [4, 5,]
-        _testAppendTask = None
-        tm._checkMemLeaks()
-
-        # task uponDeath
-        l = []
-        def _uponDeathFunc(task, l=l):
-            l.append(task.name)
-        def _testUponDeath(task):
-            return done
-        tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
-        tm.step()
-        assert len(l) == 1
-        assert l == ['testUponDeath']
-        _testUponDeath = None
-        _uponDeathFunc = None
-        tm._checkMemLeaks()
-
-        # task owner
-        class _TaskOwner:
-            def _clearTask(self, task):
-                self.clearedTaskName = task.name
-        to = _TaskOwner()
-        l = []
-        def _testOwner(task):
-            return done
-        tm.add(_testOwner, 'testOwner', owner=to)
-        tm.step()
-        assert hasattr(to, 'clearedTaskName')
-        assert to.clearedTaskName == 'testOwner'
-        _testOwner = None
-        del to
-        _TaskOwner = None
-        tm._checkMemLeaks()
-
-
-        doLaterTests = [0,]
-
-        # doLater
-        l = []
-        def _testDoLater1(task, l=l):
-            l.append(1)
-        def _testDoLater2(task, l=l):
-            l.append(2)
-        def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
-            if task.time > .03:
-                assert l == [1, 2,]
-                doLaterTests[0] -= 1
-                return task.done
-            return task.cont
-        tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
-        tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
-        doLaterTests[0] += 1
-        # make sure we run this task after the doLaters if they all occur on the same frame
-        tm.add(_monitorDoLater, 'monitorDoLater', priority=10)
-        _testDoLater1 = None
-        _testDoLater2 = None
-        _monitorDoLater = None
-        # don't check until all the doLaters are finished
-        #tm._checkMemLeaks()
-
-        # doLater priority
-        l = []
-        def _testDoLaterPri1(task, l=l):
-            l.append(1)
-        def _testDoLaterPri2(task, l=l):
-            l.append(2)
-        def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
-            if task.time > .02:
-                assert l == [1, 2,]
-                doLaterTests[0] -= 1
-                return task.done
-            return task.cont
-        tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', priority=1)
-        tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', priority=2)
-        doLaterTests[0] += 1
-        # make sure we run this task after the doLaters if they all occur on the same frame
-        tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', priority=10)
-        _testDoLaterPri1 = None
-        _testDoLaterPri2 = None
-        _monitorDoLaterPri = None
-        # don't check until all the doLaters are finished
-        #tm._checkMemLeaks()
-
-        # doLater extraArgs
-        l = []
-        def _testDoLaterExtraArgs(arg1, l=l):
-            l.append(arg1)
-        def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
-            if task.time > .02:
-                assert l == [3,]
-                doLaterTests[0] -= 1
-                return task.done
-            return task.cont
-        tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
-        doLaterTests[0] += 1
-        # make sure we run this task after the doLaters if they all occur on the same frame
-        tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', priority=10)
-        _testDoLaterExtraArgs = None
-        _monitorDoLaterExtraArgs = None
-        # don't check until all the doLaters are finished
-        #tm._checkMemLeaks()
-
-        # doLater appendTask
-        l = []
-        def _testDoLaterAppendTask(arg1, task, l=l):
-            assert task.name == 'testDoLaterAppendTask'
-            l.append(arg1)
-        def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
-            if task.time > .02:
-                assert l == [4,]
-                doLaterTests[0] -= 1
-                return task.done
-            return task.cont
-        tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
-                         extraArgs=[4,], appendTask=True)
-        doLaterTests[0] += 1
-        # make sure we run this task after the doLaters if they all occur on the same frame
-        tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', priority=10)
-        _testDoLaterAppendTask = None
-        _monitorDoLaterAppendTask = None
-        # don't check until all the doLaters are finished
-        #tm._checkMemLeaks()
-
-        # doLater uponDeath
-        l = []
-        def _testUponDeathFunc(task, l=l):
-            assert task.name == 'testDoLaterUponDeath'
-            l.append(10)
-        def _testDoLaterUponDeath(arg1, l=l):
-            return done
-        def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
-            if task.time > .02:
-                assert l == [10,]
-                doLaterTests[0] -= 1
-                return task.done
-            return task.cont
-        tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
-                         uponDeath=_testUponDeathFunc)
-        doLaterTests[0] += 1
-        # make sure we run this task after the doLaters if they all occur on the same frame
-        tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', priority=10)
-        _testUponDeathFunc = None
-        _testDoLaterUponDeath = None
-        _monitorDoLaterUponDeath = None
-        # don't check until all the doLaters are finished
-        #tm._checkMemLeaks()
-
-        # doLater owner
-        class _DoLaterOwner:
-            def _clearTask(self, task):
-                self.clearedTaskName = task.name
-        doLaterOwner = _DoLaterOwner()
-        l = []
-        def _testDoLaterOwner(l=l):
-            pass
-        def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
-                                 doLaterTests=doLaterTests):
-            if task.time > .02:
-                assert hasattr(doLaterOwner, 'clearedTaskName')
-                assert doLaterOwner.clearedTaskName == 'testDoLaterOwner'
-                doLaterTests[0] -= 1
-                return task.done
-            return task.cont
-        tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
-                         owner=doLaterOwner)
-        doLaterTests[0] += 1
-        # make sure we run this task after the doLaters if they all occur on the same frame
-        tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', priority=10)
-        _testDoLaterOwner = None
-        _monitorDoLaterOwner = None
-        del doLaterOwner
-        _DoLaterOwner = None
-        # don't check until all the doLaters are finished
-        #tm._checkMemLeaks()
-
-        # run the doLater tests
-        while doLaterTests[0] > 0:
-            tm.step()
-        del doLaterTests
-        tm._checkMemLeaks()
-
-        # getTasks
-        def _testGetTasks(task):
-            return task.cont
-        # the doLaterProcessor is always running
-        assert len(tm.getTasks()) == 1
-        tm.add(_testGetTasks, 'testGetTasks1')
-        assert len(tm.getTasks()) == 2
-        assert (tm.getTasks()[0].name == 'testGetTasks1' or
-                tm.getTasks()[1].name == 'testGetTasks1')
-        tm.add(_testGetTasks, 'testGetTasks2')
-        tm.add(_testGetTasks, 'testGetTasks3')
-        assert len(tm.getTasks()) == 4
-        tm.remove('testGetTasks2')
-        assert len(tm.getTasks()) == 3
-        tm.remove('testGetTasks1')
-        tm.remove('testGetTasks3')
-        assert len(tm.getTasks()) == 1
-        _testGetTasks = None
-        tm._checkMemLeaks()
-
-        # getDoLaters
-        def _testGetDoLaters():
-            pass
-        # the doLaterProcessor is always running
-        assert len(tm.getDoLaters()) == 0
-        tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
-        assert len(tm.getDoLaters()) == 1
-        assert tm.getDoLaters()[0].name == 'testDoLater1'
-        tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
-        tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
-        assert len(tm.getDoLaters()) == 3
-        tm.remove('testDoLater2')
-        assert len(tm.getDoLaters()) == 2
-        tm.remove('testDoLater1')
-        tm.remove('testDoLater3')
-        assert len(tm.getDoLaters()) == 0
-        _testGetDoLaters = None
-        tm._checkMemLeaks()
-
-        # duplicate named doLaters removed via taskMgr.remove
-        def _testDupNameDoLaters():
-            pass
-        # the doLaterProcessor is always running
-        tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
-        tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
-        assert len(tm.getDoLaters()) == 2
-        tm.remove('testDupNameDoLater')
-        assert len(tm.getDoLaters()) == 0
-        _testDupNameDoLaters = None
-        tm._checkMemLeaks()
-
-        # duplicate named doLaters removed via remove()
-        def _testDupNameDoLatersRemove():
-            pass
-        # the doLaterProcessor is always running
-        dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
-        dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
-        assert len(tm.getDoLaters()) == 2
-        dl2.remove()
-        assert len(tm.getDoLaters()) == 1
-        dl1.remove()
-        assert len(tm.getDoLaters()) == 0
-        _testDupNameDoLatersRemove = None
-        # nameDict etc. isn't cleared out right away with task.remove()
-        tm._checkMemLeaks()
-
-        # getTasksNamed
-        def _testGetTasksNamed(task):
-            return task.cont
-        assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-        tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-        assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
-        assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
-        tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-        tm.add(_testGetTasksNamed, 'testGetTasksNamed')
-        assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
-        tm.remove('testGetTasksNamed')
-        assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
-        _testGetTasksNamed = None
-        tm._checkMemLeaks()
-
-        # removeTasksMatching
-        def _testRemoveTasksMatching(task):
-            return task.cont
-        tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
-        assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
-        tm.removeTasksMatching('testRemoveTasksMatching')
-        assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
-        tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
-        tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
-        assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
-        assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
-        tm.removeTasksMatching('testRemoveTasksMatching*')
-        assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
-        assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
-        tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
-        tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
-        assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
-        assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
-        tm.removeTasksMatching('testRemoveTasksMatching?a')
-        assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
-        assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
-        _testRemoveTasksMatching = None
-        tm._checkMemLeaks()
-
-        # create Task object and add to mgr
-        l = []
-        def _testTaskObj(task, l=l):
-            l.append(None)
-            return task.cont
-        t = Task(_testTaskObj)
-        tm.add(t, 'testTaskObj')
-        tm.step()
-        assert len(l) == 1
-        tm.step()
-        assert len(l) == 2
-        tm.remove('testTaskObj')
-        tm.step()
-        assert len(l) == 2
-        _testTaskObj = None
-        tm._checkMemLeaks()
-        
-        # remove Task via task.remove()
-        l = []
-        def _testTaskObjRemove(task, l=l):
-            l.append(None)
-            return task.cont
-        t = Task(_testTaskObjRemove)
-        tm.add(t, 'testTaskObjRemove')
-        tm.step()
-        assert len(l) == 1
-        tm.step()
-        assert len(l) == 2
-        t.remove()
-        tm.step()
-        assert len(l) == 2
-        del t
-        _testTaskObjRemove = None
-        tm._checkMemLeaks()
-
-        """
-        # this test fails, and it's not clear what the correct behavior should be.
-        # priority passed to Task.__init__ is always overridden by taskMgr.add()
-        # even if no priority is specified, and calling Task.setPriority() has no
-        # effect on the taskMgr's behavior.
-        # set/get Task priority
-        l = []
-        def _testTaskObjPriority(arg, task, l=l):
-            l.append(arg)
-            return task.cont
-        t1 = Task(_testTaskObjPriority, priority=1)
-        t2 = Task(_testTaskObjPriority, priority=2)
-        tm.add(t1, 'testTaskObjPriority1', extraArgs=['a',], appendTask=True)
-        tm.add(t2, 'testTaskObjPriority2', extraArgs=['b',], appendTask=True)
-        tm.step()
-        assert len(l) == 2
-        assert l == ['a', 'b']
-        assert t1.getPriority() == 1
-        assert t2.getPriority() == 2
-        t1.setPriority(3)
-        assert t1.getPriority() == 3
-        tm.step()
-        assert len(l) == 4
-        assert l == ['a', 'b', 'b', 'a',]
-        t1.remove()
-        t2.remove()
-        tm.step()
-        assert len(l) == 4
-        del t1
-        del t2
-        _testTaskObjPriority = None
-        tm._checkMemLeaks()
-        """
-        
-        del l
-        tm.destroy()
-        del tm
-
-    runTests()
-    del runTests
+    pass # 'if __debug__' is hint for CVS diff output
 
 """