예제 #1
0
 def MakeResult(self, exc_info, result):
     # Make sure the exception is an object.
     if not type(exc_info[0]) is types.StringType:
         result.Fail(qm.message("test raised non-string",
                                exc_type=str(type(exc_info[0]))))
     # Make sure it's the right string.
     if exc_info[0] != self.exception_text:
         result.Fail(qm.message("test raised wrong string",
                                text=exc_info[0]))
예제 #2
0
파일: python.py 프로젝트: lcgapp/qmtest
 def MakeResult(self, exc_info, result):
     # Make sure the exception is an object.
     if not type(exc_info[0]) is types.StringType:
         result.Fail(
             qm.message("test raised non-string",
                        exc_type=str(type(exc_info[0]))))
     # Make sure it's the right string.
     if exc_info[0] != self.exception_text:
         result.Fail(
             qm.message("test raised wrong string", text=exc_info[0]))
예제 #3
0
 def MakeResult(self, exc_info, result):
     # Make sure the exception is an object.
     if not type(exc_info[0]) is types.ClassType:
         result.Fail(qm.message("test raised non-object",
                                exc_type=str(type(exc_info[0]))))
     # Make sure it's an instance of the right class.
     exception_class_name = exc_info[0].__name__
     if exception_class_name != self.exception_class:
         cause = qm.message("test raised wrong class",
                            class_name=exception_class_name)
         result.Fail(cause=cause)
예제 #4
0
파일: python.py 프로젝트: lcgapp/qmtest
 def MakeResult(self, exc_info, result):
     # Make sure the exception is an object.
     if not type(exc_info[0]) in [types.ClassType, type]:
         result.Fail(
             qm.message("test raised non-object",
                        exc_type=str(type(exc_info[0]))))
     # Make sure it's an instance of the right class.
     exception_class_name = exc_info[0].__name__
     if exception_class_name != self.exception_class:
         cause = qm.message("test raised wrong class",
                            class_name=exception_class_name)
         result.Fail(cause=cause)
예제 #5
0
    def _SetUpResource(self, resource_name, context):
        """Set up the resource given by 'resource_id'.

        'resource_name' -- The name of the resource to be set up.

        'context' -- The 'Context' in which to run the resource.

        returns -- A map from strings to strings indicating additional
        properties added by this resource."""

        # Begin setting up the resource.
        rop = self._BeginResourceSetUp(resource_name)
        # If it has already been set up, there is no need to do it
        # again.
        if rop:
            return rop
        # Set up the context.
        wrapper = Context(context)
        result = Result(Result.RESOURCE_SETUP, resource_name, Result.PASS)
        resource = None
        # Get the resource descriptor.
        try:
            resource_desc = self.GetDatabase().GetResource(resource_name)
            # Set up the resources on which this resource depends.
            self.__SetUpResources(resource_desc, wrapper)
            # Make the ID of the resource available.
            wrapper[Context.ID_CONTEXT_PROPERTY] = resource_name
            # Set up the resource itself.
            resource_desc.SetUp(wrapper, result)
            # Obtain the resource within the try-block so that if it
            # cannot be obtained the exception is handled below.
            resource = resource_desc.GetItem()
        except self.__ResourceSetUpException, e:
            result.Fail(qm.message("failed resource"),
                        {result.RESOURCE: e.resource})
예제 #6
0
파일: python.py 프로젝트: lcgapp/qmtest
    def CheckArgument(self, exc_info, result):
        """Check that the exception argument matches expectations.

        'result' -- The result object for this test."""

        # Was an expected exception argument specified?
        if self.has_exception_argument:
            # Yes.  Extract the exception argument.
            argument = exc_info[1]
            if cmp(argument, self.exception_argument):
                cause = qm.message("test raised wrong argument")
                result.Fail(
                    cause, {
                        "BaseExceptionTest.type": str(exc_info[0]),
                        "BaseExceptionTest.argument": repr(argument)
                    })
예제 #7
0
    def CheckArgument(self, exc_info, result):
        """Check that the exception argument matches expectations.

        'result' -- The result object for this test."""

        # Was an expected exception argument specified?
        if self.has_exception_argument:
            # Yes.  Extract the exception argument.
            argument = exc_info[1]
            if cmp(argument, self.exception_argument):
                cause = qm.message("test raised wrong argument")
                result.Fail(cause,
                            { "BaseExceptionTest.type" :
                              str(exc_info[0]),
                              "BaseExceptionTest.argument" :
                               repr(argument) })
예제 #8
0
파일: python.py 프로젝트: lcgapp/qmtest
    def CheckArgument(self, exc_info, result):
        """Check that the exception argument matches expectations.

        'result' -- The result object for this test."""

        # Was an expected argument specified?
        if self.has_exception_argument:
            # Extract the actual argument from the exception object.
            try:
                argument = exc_info[1].args
            except:
                # If the "args" were not available, then the exception
                # object does not use the documented interface given
                # for Exception.
                result.Fail(
                    "Exception object does not provide access "
                    "to arguments provided to 'raise'",
                    {"ExceptionTest.type": str(exc_info[0])})
                return

            # Now the expected argument.
            expected_argument = self.exception_argument
            # Python wraps the arguments to class exceptions in strange
            # ways, so wrap the expected argument similarly.  A 'None'
            # argument is represented by an empty tuple.
            if expected_argument is None:
                expected_argument = ()
            # Tuple arguments are unmodified.
            elif type(expected_argument) is types.TupleType:
                pass
            # A non-tuple argument is wrapped in a tuple.
            else:
                expected_argument = (expected_argument, )

            # Compare the actual argument to the expectation.
            if cmp(expected_argument, argument) != 0:
                # We got a different argument.  The test fails.
                cause = qm.message("test raised wrong argument")
                result.Fail(
                    cause, {
                        "ExceptionTest.type": str(exc_info[0]),
                        "ExceptionTest.argument": repr(argument)
                    })
예제 #9
0
    def CheckArgument(self, exc_info, result):
        """Check that the exception argument matches expectations.

        'result' -- The result object for this test."""

        # Was an expected argument specified?
        if self.has_exception_argument:
            # Extract the actual argument from the exception object.
            try:
                argument = exc_info[1].args
            except:
                # If the "args" were not available, then the exception
                # object does not use the documented interface given
                # for Exception.
                result.Fail("Exception object does not provide access "
                            "to arguments provided to 'raise'",
                            { "ExceptionTest.type" : str(exc_info[0]) })
                return
                
            # Now the expected argument.
            expected_argument = self.exception_argument
            # Python wraps the arguments to class exceptions in strange
            # ways, so wrap the expected argument similarly.  A 'None'
            # argument is represented by an empty tuple.
            if expected_argument is None:
                expected_argument = ()
            # Tuple arguments are unmodified.
            elif type(expected_argument) is types.TupleType:
                pass
            # A non-tuple argument is wrapped in a tuple.
            else:
                expected_argument = (expected_argument, )

            # Compare the actual argument to the expectation.
            if cmp(expected_argument, argument) != 0:
                # We got a different argument.  The test fails.
                cause = qm.message("test raised wrong argument")
                result.Fail(cause,
                            { "ExceptionTest.type" : str(exc_info[0]),
                              "ExceptionTest.argument" : repr(argument) })
예제 #10
0
    def _SetUpResource(self, resource_name, context):
        """Set up the resource given by 'resource_id'.

        'resource_name' -- The name of the resource to be set up.

        'context' -- The 'Context' in which to run the resource.

        returns -- A map from strings to strings indicating additional
        properties added by this resource."""

        # Begin setting up the resource.
        rop = self._BeginResourceSetUp(resource_name)
        # If it has already been set up, there is no need to do it
        # again.
        if rop:
            return rop
        # Set up the context.
        wrapper = Context(context)
        result = Result(Result.RESOURCE_SETUP, resource_name, Result.PASS)
        resource = None
        # Get the resource descriptor.
        try:
            resource_desc = self.GetDatabase().GetResource(resource_name)
            # Set up the resources on which this resource depends.
            self.__SetUpResources(resource_desc, wrapper)
            # Make the ID of the resource available.
            wrapper[Context.ID_CONTEXT_PROPERTY] = resource_name
            # Set up the resource itself.
            try:
                resource_desc.SetUp(wrapper, result)
            finally:
                del wrapper[Context.ID_CONTEXT_PROPERTY]
            # Obtain the resource within the try-block so that if it
            # cannot be obtained the exception is handled below.
            resource = resource_desc.GetItem()
        except self.__ResourceSetUpException, e:
            result.Fail(qm.message("failed resource"),
                        { result.RESOURCE : e.resource })
예제 #11
0
파일: python.py 프로젝트: lcgapp/qmtest
    def Run(self, context, result):

        # Adjust the exception argument.
        if string.strip(self.exception_argument) != "":
            self.exception_argument = eval(self.exception_argument, {}, {})
            self.has_exception_argument = 1
        else:
            self.has_exception_argument = 0

        global_namespace, local_namespace = make_namespaces(context)
        try:
            # Execute the test code.
            exec self.source in global_namespace, local_namespace
        except:
            exc_info = sys.exc_info()
            # Check the exception argument.
            self.CheckArgument(exc_info, result)
            if result.GetOutcome() != Result.PASS:
                return
            # Check that the exception itself is OK.
            self.MakeResult(exc_info, result)
        else:
            # The test code didn't raise an exception.
            result.Fail(qm.message("test did not raise"))
예제 #12
0
    def Run(self, context, result):

        # Adjust the exception argument.
        if string.strip(self.exception_argument) != "":
            self.exception_argument = eval(self.exception_argument, {}, {})
            self.has_exception_argument = 1
        else:
            self.has_exception_argument = 0
            
        global_namespace, local_namespace = make_namespaces(context)
        try:
            # Execute the test code.
            exec self.source in global_namespace, local_namespace
        except:
            exc_info = sys.exc_info()
            # Check the exception argument.
            self.CheckArgument(exc_info, result)
            if result.GetOutcome() != Result.PASS:
                return
            # Check that the exception itself is OK.
            self.MakeResult(exc_info, result)
        else:
            # The test code didn't raise an exception.
            result.Fail(qm.message("test did not raise"))
예제 #13
0
    qm.rc.Load("test")

    try:
        exit_code = main()
    except qm.cmdline.CommandError, msg:
        print_error_message(msg)
        sys.stderr.write(
            "Run 'qmtest --help' to get instructions about how to use QMTest.\n"
        )
    except qm.common.QMException, msg:
        print_error_message(msg)
    except NotImplementedError:
        exc_info = sys.exc_info()
        method_name = traceback.extract_tb(exc_info[2])[-1][2]
        print_error_message(
            qm.message("not implemented", method_name=method_name))
        sys.stderr.write(qm.common.format_traceback(exc_info))
    except KeyboardInterrupt:
        sys.stderr.write("\nqmtest: Interrupted.\n")
        raise
    except qm.platform.SignalException, se:
        # SIGTERM indicates a request to shut down.  Other signals
        # should be handled earlier.
        if se.GetSignalNumber() != signal.SIGTERM:
            raise
finally:
    # Collect garbage so that any "__del__" methods with externally
    # visible side-effects are executed.
    del qm.test.cmdline._the_qmtest
    gc.collect()
예제 #14
0
            # Note the exception.
            result.NoteException(cause = str(e))
            # If we get a SIGTERM, propagate it so that QMTest
            # terminates.
            if e.GetSignalNumber() == signal.SIGTERM:
                # Record the result so that the traceback is
                # available.
                self._RecordResult(result)
                # Ask the execution engine to stop running tests.
                if self.__engine:
                    self.__engine.RequestTermination()
                # Re-raise the exception.
                raise
        except self.__ResourceSetUpException, e:
            result.SetOutcome(Result.UNTESTED)
            result[Result.CAUSE] = qm.message("failed resource")
            result[Result.RESOURCE] = e.resource
        except:
            result.NoteException()
        # Record the result.
        self._RecordResult(result)


    def _RecordResult(self, result):
        """Record the 'result'.

        'result' -- A 'Result' of a test or resource execution.

        Derived classes may override this method, but the overriding
        method must call this method at some point during its
        execution."""
예제 #15
0
            # Note the exception.
            result.NoteException(cause=str(e))
            # If we get a SIGTERM, propagate it so that QMTest
            # terminates.
            if e.GetSignalNumber() == signal.SIGTERM:
                # Record the result so that the traceback is
                # available.
                self._RecordResult(result)
                # Ask the execution engine to stop running tests.
                if self.__engine:
                    self.__engine.RequestTermination()
                # Re-raise the exception.
                raise
        except self.__ResourceSetUpException, e:
            result.SetOutcome(Result.UNTESTED)
            result[Result.CAUSE] = qm.message("failed resource")
            result[Result.RESOURCE] = e.resource
        except:
            result.NoteException()
        # Record the result.
        self._RecordResult(result)

    def _RecordResult(self, result):
        """Record the 'result'.

        'result' -- A 'Result' of a test or resource execution.

        Derived classes may override this method, but the overriding
        method must call this method at some point during its
        execution."""
예제 #16
0
    # Load RC options.
    qm.rc.Load("test")

    try:
        exit_code = main()
    except qm.cmdline.CommandError, msg:
        print_error_message(msg)
        sys.stderr.write(
            "Run 'qmtest --help' to get instructions about how to use QMTest.\n")
    except qm.common.QMException, msg:
        print_error_message(msg)
    except NotImplementedError:
        exc_info = sys.exc_info()
        method_name = traceback.extract_tb(exc_info[2])[-1][2]
        print_error_message(qm.message("not implemented",
                                       method_name = method_name))
        sys.stderr.write(qm.common.format_traceback(exc_info))
    except KeyboardInterrupt:
        sys.stderr.write("\nqmtest: Interrupted.\n")
        raise
    except qm.platform.SignalException, se:
        # SIGTERM indicates a request to shut down.  Other signals
        # should be handled earlier.
        if se.GetSignalNumber() != signal.SIGTERM:
            raise
finally:
    # Collect garbage so that any "__del__" methods with externally
    # visible side-effects are executed.
    del qm.test.cmdline._the_qmtest
    gc.collect()
예제 #17
0
class Target(qm.extension.Extension):
    """Base class for target implementations.

    A 'Target' is an entity that can run tests.  QMTest can spread the
    workload from multiple tests across multiple targets.  In
    addition, a single target can run more that one test at once.

    'Target' is an abstract class.

    You can extend QMTest by providing your own target class
    implementation.

    To create your own test class, you must create a Python class
    derived (directly or indirectly) from 'Target'.  The documentation
    for each method of 'Target' indicates whether you must override it
    in your test class implementation.  Some methods may be
    overridden, but do not need to be.  You might want to override
    such a method to provide a more efficient implementation, but
    QMTest will work fine if you just use the default version."""

    arguments = [
        qm.fields.TextField(name="name",
                            title="Name",
                            description="""The name of this target.

            The name of the target.  The target name will be recorded
            in any tests executed on that target so that you can see
            where the test was run.""",
                            default_value=""),
        qm.fields.TextField(
            name="group",
            title="Group",
            description="""The group associated with this target.

            Some tests may only be able to run on some targets.  A
            test can specify a pattern indicating the set of targets
            on which it will run.""",
            default_value="")
    ]

    kind = "target"

    class __ResourceSetUpException(Exception):
        """An exception indicating that a resource could not be set up."""
        def __init__(self, resource):
            """Construct a new 'ResourceSetUpException'.

            'resource' -- The name of the resoure that could not be
            set up."""

            self.resource = resource

    def __init__(self, database, properties):
        """Construct a 'Target'.

        'database' -- The 'Database' containing the tests that will be
        run.

        'properties'  -- A dictionary mapping strings (property names)
        to values."""

        qm.extension.Extension.__init__(self, properties)

        self.__database = database
        #38747
        self.log = logging.getLogger('nextestlog')

    def GetName(self):
        """Return the name of the target.

        Derived classes must not override this method."""

        return self.name

    def GetGroup(self):
        """Return the group of which the target is a member.

        Derived classes must not override this method."""

        return self.group

    def GetDatabase(self):
        """Return the 'Database' containing the tests this target will run.

        returns -- The 'Database' containing the tests this target will
        run.

        Derived classes must not override this method."""

        return self.__database

    def IsIdle(self):
        """Return true if the target is idle.

        returns -- True if the target is idle.  If the target is idle,
        additional tasks may be assigned to it.

        Derived classes must override this method."""

        raise NotImplementedError

    def IsInGroup(self, group_pattern):
        """Returns true if this 'Target' is in a particular group.

        'group_pattern' -- A string giving a regular expression.

        returns -- Returns true if the 'group_pattern' denotes a
        regular expression that matches the group for this 'Target',
        false otherwise."""

        return re.match(group_pattern, self.GetGroup())

    def Start(self, response_queue, engine=None):
        """Start the target.

        'response_queue' -- The 'Queue' in which the results of test
        executions are placed.
        
        'engine' -- The 'ExecutionEngine' that is starting the target,
        or 'None' if this target is being started without an
        'ExecutionEngine'.
        
        Derived classes may override this method, but the overriding
        method must call this method at some point during its
        execution."""

        self.__response_queue = response_queue
        self.__engine = engine
        # There are no resources available on this target yet.
        self.__resources = {}
        self.__order_of_resources = []

    def Stop(self):
        """Stop the target.

        Clean up all resources that have been set up on this target
        and take whatever other actions are required to stop the
        target.
        
        Derived classes may override this method."""

        # Clean up any available resources.
        self.__order_of_resources.reverse()
        for name in self.__order_of_resources:
            rop = self.__resources[name]
            if rop and rop[1] == Result.PASS:
                self._CleanUpResource(name, rop[0])
        del self.__response_queue
        del self.__engine
        del self.__resources
        del self.__order_of_resources

    def RunTest(self, descriptor, context):
        """Run the test given by 'test_id'.

        'descriptor' -- The 'TestDescriptor' for the test.

        'context' -- The 'Context' in which to run the test.

        Derived classes may override this method."""

        # Create the result.
        result = Result(Result.TEST, descriptor.GetId())
        try:
            #38747
            scm = context['nextest.scm_configuration']
            # Augment the context appropriately.
            context = Context(context)
            context[context.TMPDIR_CONTEXT_PROPERTY] \
                = self._GetTemporaryDirectory()
            context[context.DB_PATH_CONTEXT_PROPERTY] \
                = descriptor.GetDatabase().GetPath()
            # Set up any required resources.
            self.__SetUpResources(descriptor, context)
            # Make the ID of the test available.
            context[context.ID_CONTEXT_PROPERTY] = descriptor.GetId()
            # Note the start time.
            result[Result.START_TIME] = qm.common.format_time_iso()

            # 38747 If SCM configuration is ON and the swap flag is enabled
            # continue the remaining tests on the backup by swapping the mysmw and bkupmsw
            # objects and also in /etc/hosts file. Stop the corewatcher on the master and start the
            # corewatcher on the backup in order to monitor for core on the backup
            if scm and self.__engine.swap:
                if context.has_key('userConfig.to_addr'):
                    toaddr = context['userConfig.to_addr']
                    toaddrs = string.split(toaddr, ',')
                    if ((toaddr == '') or (string.count(toaddr, '@') == 0)):
                        self.log.info("ERROR : Entered to address %s is wrong,\
                             " % toaddr)
                if context.has_key('userConfig.from_addr'):
                    fromaddr = context['userConfig.from_addr']
                    if ((fromaddr == '')
                            or (string.count(fromaddr, '@') == 0)):
                        self.log.info(
                            "ERROR : Entered from address %s is wrong,\
                             " % fromaddr)
                if context.has_key('userConfig.mail_server'):
                    mailserver = context['userConfig.mail_server']
                    if (mailserver == ''):
                        self.log.info(
                            "ERROR : Entered mailserver name %s is wrong,\
                             " % mailserver)
                # Added the code for ticket 34247
                if context.has_key('userConfig.login'):
                    login = context['userConfig.login']
                    if (login == ''):
                        self.log.info(
                            "ERROR : Entered  login name %s is wrong,\
                             " % login)
                if context.has_key('userConfig.passwd'):
                    passwd = context['userConfig.passwd']
                    if (passwd == ''):
                        self.log.info(
                            "ERROR : Entered passwd value  %s is wrong,\
                             " % passwd)

                if ((toaddr == '') or (fromaddr == '') or (mailserver == '')
                        or (login == '') or (passwd == '')):
                    toaddr = None
                    fromaddr = None
                    mailserver = None
                    login = None
                    passwd = None
                stopOnCore = 'True'
                if context.has_key('userConfig.stop_on_core'):
                    stopOnCore = context['userConfig.stop_on_core']

                #if restartSCM is found in the script, then reverting back the original
                #/etc/hosts file and stopping the corewatcher on the bkup. Start the
                #corewatcher on the master to monitor the core on the master
                tempStr = str(descriptor.GetClass())
                if tempStr.__contains__('ExecTest'):
                    contStr = descriptor.GetItem().source
                else:
                    contStr = descriptor.GetItem().testsource
                if contStr.__contains__('restartSCM'):
                    self.__engine.swap = False
                    # Restore /etc/hosts
                    if self.__engine.hostsFile:
                        self.__engine.hostsFile = False
                        os.system('sudo cp /etc/hosts_scm /etc/hosts')
                        os.system('sudo rm -rf /etc/hosts_scm')

                # Modify /etc/hosts only once!
                # Modified the /etc/hosts file to continue the remaining tests
                # in bkup until the iserver restart is performed during the presence
                # of core
                if self.__engine.swap:
                    if not self.__engine.hostsFile:
                        modifyHostsFile()
                        self.__engine.hostsFile = True
                    #43909 Added code to revert back the original /etc/hosts file
                    #when core is found from active secondary MSX
                    else:
                        if self.__engine.revertBack:
                            self.__engine.swap = False
                            os.system('sudo cp /etc/hosts_scm /etc/hosts')
                            os.system('sudo rm -rf /etc/hosts_scm')
                            self.__engine.hostsFile = False
                            self.__engine.bkupObj = None
                            self.__engine.revertBack = False

                    # Swapping the MSW and MSWInfo object during the presence of core
                    if not self.__engine.bkupObj:
                        self.__engine.bkupCtrl = msw.MSWInfo('mymsw')
                        cont = {'mswinfo': self.__engine.bkupCtrl}
                        self.__engine.bkupObj = SSH(['root@mymsw'], ctxt=cont)
                    context['msw'] = self.__engine.bkupObj
                    context['mswinfo'] = self.__engine.bkupCtrl
            # Run the test.
            try:
                # Pass on the information on whether the previous script restarted MSW or not
                context['msw_restart'] = self.__engine.msw_restart
                descriptor.Run(context, result)

                # 39583 - Check whether the script restarts only if H323 call has been made
                # msw_restart - Variable used to store previous script information
                # hValNotChecked - Used to indicate if the first H323 script after the iserver restart has been
                # executed or not
                tempStr = str(descriptor.GetClass())
                if tempStr.__contains__('call.CallTest'):
                    if context[
                            'hValNotChecked'] or not self.__engine.msw_restart:
                        if tempStr.__contains__('ExecTest'):
                            contStr = descriptor.GetItem().source
                        else:
                            contStr = descriptor.GetItem().testsource

                        src = contStr.lower()
                        chk1 = src.__contains__(
                            'iserver all stop') or src.__contains__(
                                'ftest') or src.__contains__('checkiserver')
                        chk2 = src.__contains__(
                            'checkscmstatus') or src.__contains__(
                                'restartscm') or src.__contains__(
                                    '.initialize')
                        if chk1 or chk2:
                            self.__engine.msw_restart = True
                        else:
                            self.__engine.msw_restart = False
            finally:
                # Note the end time.
                result[Result.END_TIME] = qm.common.format_time_iso()
        except KeyboardInterrupt:
            result.NoteException(cause="Interrupted by user.")
            # We received a KeyboardInterrupt, indicating that the
            # user would like to exit QMTest.  Ask the execution
            # engine to stop.
            if self.__engine:
                self.__engine.RequestTermination()
        except qm.platform.SignalException, e:
            # Note the exception.
            result.NoteException(cause=str(e))
            # If we get a SIGTERM, propagate it so that QMTest
            # terminates.
            if e.GetSignalNumber() == signal.SIGTERM:
                # Record the result so that the traceback is
                # available.
                self._RecordResult(result)
                # Ask the execution engine to stop running tests.
                if self.__engine:
                    self.__engine.RequestTermination()
                # Re-raise the exception.
                raise
        except self.__ResourceSetUpException, e:
            result.SetOutcome(Result.UNTESTED)
            result[Result.CAUSE] = qm.message("failed resource")
            result[Result.RESOURCE] = e.resource
예제 #18
0
    def __str__(self):
        """Return a string describing this exception."""

        return qm.message("no such item",
                          kind = self.kind,
                          item_id = self.item_id)
예제 #19
0
    # Load RC options.
    qm.rc.Load("test")

    try:
        exit_code = main()
    except qm.cmdline.CommandError, msg:
        print_error_message(msg)
        sys.stderr.write(
            "Run 'qmtest --help' to get instructions about how to use QMTest.\n")
    except qm.common.QMException, msg:
        print_error_message(msg)
    except NotImplementedError:
        exc_info = sys.exc_info()
        method_name = traceback.extract_tb(exc_info[2])[-1][2]
        print_error_message(qm.message("not implemented",
                                       method_name = method_name))
        sys.stderr.write(qm.common.format_traceback(exc_info))
    except KeyboardInterrupt:
        sys.stderr.write("\nqmtest: Interrupted.\n")
        raise
    except qm.platform.SignalException, se:
        # SIGTERM indicates a request to shut down.  Other signals
        # should be handled earlier.
        if se.GetSignalNumber() != signal.SIGTERM:
            raise
finally:
    # Collect garbage so that any "__del__" methods with externally
    # visible side-effects are executed.
    del qm.test.cmdline._the_qmtest
    gc.collect()