[asterisk-commits] mjordan: testsuite/asterisk/trunk r3188 - /asterisk/trunk/lib/python/asterisk/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Apr 17 18:05:48 CDT 2012


Author: mjordan
Date: Tue Apr 17 18:05:44 2012
New Revision: 3188

URL: http://svnview.digium.com/svn/testsuite?view=rev&rev=3188
Log:
Update the pre-/post-condition check framework to support twisted

This patch updates the pre-/post-condition checking framework in the
Asterisk Test Suite to use the twisted asynchronous framework for its
CLI/AMI commands.  This was necessary, as the starting/stopping
of Asterisk now expects things that explicitly need to be involved
in the process to be in the deferred callback chain.

Review: https://reviewboard.asterisk.org/r/1831

Modified:
    asterisk/trunk/lib/python/asterisk/ChannelTestCondition.py
    asterisk/trunk/lib/python/asterisk/FdTestCondition.py
    asterisk/trunk/lib/python/asterisk/LockTestCondition.py
    asterisk/trunk/lib/python/asterisk/SipDialogTestCondition.py
    asterisk/trunk/lib/python/asterisk/TestCase.py
    asterisk/trunk/lib/python/asterisk/TestConditions.py
    asterisk/trunk/lib/python/asterisk/ThreadTestCondition.py

Modified: asterisk/trunk/lib/python/asterisk/ChannelTestCondition.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/ChannelTestCondition.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/ChannelTestCondition.py (original)
+++ asterisk/trunk/lib/python/asterisk/ChannelTestCondition.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2011, Digium, Inc.
+Copyright (C) 2011-2012, Digium, Inc.
 Matt Jordan <mjordan at digium.com>
 
 This program is free software, distributed under the terms of
@@ -11,6 +11,7 @@
 import logging.config
 import unittest
 
+from twisted.internet import defer
 from TestConditions import TestCondition
 
 logger = logging.getLogger(__name__)
@@ -32,10 +33,8 @@
             self.allowed_channels = test_config.config['allowedchannels']
 
     def evaluate(self, related_test_condition = None):
-        for ast in self.ast:
-            """ For logging / debug purposes, do a full core show channels """
-            channel_lines = ast.cli_exec('core show channels')
-            channel_tokens = channel_lines.strip().split('\n')
+        def __channel_callback(result):
+            channel_tokens = result.output.strip().split('\n')
             active_channels = 0
             for token in channel_tokens:
                 if 'active channels' in token:
@@ -43,7 +42,18 @@
                     active_channels = int(active_channel_tokens[0].strip())
             if active_channels > self.allowed_channels:
                 super(ChannelTestCondition, self).failCheck(
-                    'Detected number of active channels %d is greater than the allowed %d on Asterisk %s' % (active_channels, self.allowed_channels, ast.host))
-        """ Set to pass if we haven't detected any failures """
+                    'Detected number of active channels %d is greater than the allowed %d on Asterisk %s'
+                     % (active_channels, self.allowed_channels, result.host))
+            return result
+
+        def __raise_finished(result):
+            self.__finished_deferred.callback(self)
+            return result
+
+        self.__finished_deferred = defer.Deferred()
+        # Set to pass and let a failure override
         super(ChannelTestCondition, self).passCheck()
 
+        defer.DeferredList([ast.cli_exec('core show channels').addCallback(__channel_callback) for ast in self.ast]
+                           ).addCallback(__raise_finished)
+        return self.__finished_deferred

Modified: asterisk/trunk/lib/python/asterisk/FdTestCondition.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/FdTestCondition.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/FdTestCondition.py (original)
+++ asterisk/trunk/lib/python/asterisk/FdTestCondition.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2011, Digium, Inc.
+Copyright (C) 2011-2012, Digium, Inc.
 Matt Jordan <mjordan at digium.com>
 
 This program is free software, distributed under the terms of
@@ -11,6 +11,7 @@
 import logging.config
 import unittest
 
+from twisted.internet import defer
 from TestConditions import TestCondition
 
 logger = logging.getLogger(__name__)
@@ -47,73 +48,83 @@
         self.add_build_option("DEBUG_FD_LEAKS", "1")
 
     def get_file_descriptors(self, ast):
-        if ast == None:
-            return
+        def __show_fd_callback(result):
+            lines = result.output
+            if 'No such command' in lines:
+                return result
+            if 'Unable to connect to remote asterisk' in lines:
+                return result
 
-        lines = ast.cli_exec("core show fd")
-        if 'No such command' in lines:
-            return
-        if 'Unable to connect to remote asterisk' in lines:
-            return
+            """ Trim off the first and last lines """
+            lines = lines[lines.find('\n'):].strip()
+            lines = lines[:lines.find("Asterisk ending")].strip()
+            line_tokens = lines.split('\n')
+            fds = []
+            for line in line_tokens:
+                # chan_sip is going to create sockets for the active channels and won't close them until
+                # the dialog is reclaimed - 32 seconds after the test.  We ignore the UDP socket file
+                # descriptors because of this.
+                if 'socket(PF_INET,SOCK_DGRAM,"udp")' in line:
+                    logger.debug("Ignoring created UDP socket: " + line)
+                    continue
+                # If we have MALLOC_DEBUG on and are writing out to the mmlog, ignore
+                if '__ast_mm_init' in line:
+                    logger.debug("Ignoring malloc debug: " + line)
+                    continue
+                fd = FileDescriptor(line)
+                if fd.number != -1:
+                    logger.debug("Tracking %d [%s]", fd.number, fd.info)
+                    fds.append(fd)
+                else:
+                    logger.warn("Failed to parse [%s] into file descriptor object" % line)
+            self.file_descriptors[result.host] = fds
 
-        """ Trim off the first and last lines """
-        lines = lines[lines.find('\n'):].strip()
-        lines = lines[:lines.find("Asterisk ending")].strip()
-        line_tokens = lines.split('\n')
-        fds = []
-        for line in line_tokens:
-            """
-            chan_sip is going to create sockets for the active channels and won't close them until
-            the dialog is reclaimed - 32 seconds after the test.  We ignore the UDP socket file
-            descriptors because of this.
-            """
-            if 'socket(PF_INET,SOCK_DGRAM,"udp")' in line:
-                logger.debug("Ignoring created UDP socket: " + line)
-                continue
-            """
-            If we have MALLOC_DEBUG on and are writing out to the mmlog, ignore
-            """
-            if '__ast_mm_init' in line:
-                logger.debug("Ignoring malloc debug: " + line)
-                continue
-            fd = FileDescriptor(line)
-            if fd.number != -1:
-                logger.debug("Tracking %d [%s]", fd.number, fd.info)
-                fds.append(fd)
-            else:
-                logger.warn("Failed to parse [%s] into file descriptor object" % line)
-        self.file_descriptors[ast.host] = fds
+        return ast.cli_exec("core show fd").addCallback(__show_fd_callback)
 
 class FdPreTestCondition(FdTestCondition):
     def evaluate(self, related_test_condition = None):
-        for ast in self.ast:
-            super(FdPreTestCondition, self).get_file_descriptors(ast)
+        def __raise_finished(result):
+            self.__finished_deferred.callback(self)
+            return result
 
-        """
-        Automatically pass the pre-test condition - whatever file descriptors are currently
-        open are needed by Asterisk and merely expected to exist when the test is finished
-        """
+        #Automatically pass the pre-test condition - whatever file descriptors are currently
+        #open are needed by Asterisk and merely expected to exist when the test is finished
         super(FdPreTestCondition, self).passCheck()
+
+        defer.DeferredList([super(FdPreTestCondition, self).get_file_descriptors(ast)
+            for ast in self.ast]).addCallback(__raise_finished)
+
+        self.__finished_deferred = defer.Deferred()
+        return self.__finished_deferred
 
 class FdPostTestCondition(FdTestCondition):
     def evaluate(self, related_test_condition = None):
+        def __file_descriptors_obtained(result):
+            for ast_host in related_test_condition.file_descriptors.keys():
+                if not ast_host in self.file_descriptors:
+                    super(FdPostTestCondition, self).failCheck("Asterisk host in pre-test check [%s]"
+                        " not found in post-test check" % ast_host)
+                else:
+                    # Find all file descriptors in pre-check not in post-check
+                    for fd in related_test_condition.file_descriptors[ast_host]:
+                        if (len([f for f in self.file_descriptors[ast_host] if fd.number == f.number]) == 0):
+                            super(FdPostTestCondition, self).failCheck("Failed to find file descriptor %d [%s] in "
+                                "post-test check" % (fd.number, fd.info))
+                    # Find all file descriptors in post-check not in pre-check
+                    for fd in self.file_descriptors[ast_host]:
+                        if (len([f for f in related_test_condition.file_descriptors[ast_host] if fd.number == f.number]) == 0):
+                            super(FdPostTestCondition, self).failCheck("Failed to find file descriptor %d [%s] in "
+                            "pre-test check" % (fd.number, fd.info))
+            super(FdPostTestCondition, self).passCheck()
+            self.__finished_deferred.callback(self)
+            return result
+
         if related_test_condition == None:
             super(FdPostTestCondition, self).failCheck("No pre-test condition object provided")
             return
 
-        for ast in self.ast:
-            super(FdPostTestCondition, self).get_file_descriptors(ast)
+        defer.DeferredList([super(FdPostTestCondition, self).get_file_descriptors(ast) for ast in self.ast]
+                           ).addCallback(__file_descriptors_obtained)
+        self.__finished_deferred = defer.Deferred()
+        return self.__finished_deferred
 
-        for ast_host in related_test_condition.file_descriptors.keys():
-            if not ast_host in self.file_descriptors:
-                super(FdPostTestCondition, self).failCheck("Asterisk host in pre-test check [%s] not found in post-test check" % ast_host)
-            else:
-                for fd in related_test_condition.file_descriptors[ast_host]:
-                    match = [f for f in self.file_descriptors[ast_host] if fd.number == f.number]
-                    if (len(match) == 0):
-                        super(FdPostTestCondition, self).failCheck("Failed to find file descriptor %d [%s] in post-test check" % (fd.number, fd.info))
-                for fd in self.file_descriptors[ast_host]:
-                    match = [f for f in related_test_condition.file_descriptors[ast_host] if fd.number == f.number]
-                    if (len(match) == 0):
-                        super(FdPostTestCondition, self).failCheck("Failed to find file descriptor %d [%s] in pre-test check" % (fd.number, fd.info))
-        super(FdPostTestCondition, self).passCheck()

Modified: asterisk/trunk/lib/python/asterisk/LockTestCondition.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/LockTestCondition.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/LockTestCondition.py (original)
+++ asterisk/trunk/lib/python/asterisk/LockTestCondition.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2011, Digium, Inc.
+Copyright (C) 2011-2012, Digium, Inc.
 Matt Jordan <mjordan at digium.com>
 
 This program is free software, distributed under the terms of
@@ -11,6 +11,7 @@
 import logging.config
 import unittest
 
+from twisted.internet import defer
 from TestConditions import TestCondition
 
 logger = logging.getLogger(__name__)
@@ -153,43 +154,55 @@
         self.add_build_option("DEBUG_THREADS", "1")
 
     def __get_locks(self, ast):
-        locks = ast.cli_exec("core show locks", True)
-        """ The first 6 lines are header information - look for a return thread ID """
-        if "=== Thread ID:" in locks:
-            locks = locks[locks.find("=== Thread ID:"):]
-            lockTokens = locks.split("=== -------------------------------------------------------------------")
-            for token in lockTokens:
-                if "Thread ID" in token:
-                    try:
-                        obj = LockSequence()
-                        obj.parseLockSequence(token)
-                        t = ast.host, obj
-                        self.locks.append(t)
-                    except:
-                        logger.warning("Unable to parse lock information into a manageable object:\n%s" % token)
+        """ Build the locks for an instance of Asterisk
+        Returns:
+        A deferred for when the locks are built for a given instance
+        """
+        def __show_locks_callback(result):
+            locks = result.output
+            # The first 6 lines are header information - look for a return thread ID
+            if "=== Thread ID:" in locks:
+                locks = locks[locks.find("=== Thread ID:"):]
+                lockTokens = locks.split("=== -------------------------------------------------------------------")
+                for token in lockTokens:
+                    if "Thread ID" in token:
+                        try:
+                            obj = LockSequence()
+                            obj.parseLockSequence(token)
+                            t = result.host, obj
+                            self.locks.append(t)
+                        except:
+                            logger.warning("Unable to parse lock information into a manageable object:\n%s" % token)
+            return result
+
+        return ast.cli_exec("core show locks").addCallback(__show_locks_callback)
 
     def evaluate(self, related_test_condition = None):
-        """ Build up the locks for each instance of asterisk """
-        for ast in self.ast:
-            self.__get_locks(ast)
-
-        if (len(self.locks) > 0):
-            """
-            Sometimes, a lock will be held at the end of a test run (typically a logger RDLCK).  Only
-            report a held lock as a failure if the thread is waiting for another lock - that would
-            indicate that we may be in a deadlock situation.  Since that shouldnt happen either
-            before or after a test run, treat that as an error.
-            """
-            for lockPair in self.locks:
-                logger.info("Detected locks on Asterisk instance: %s" % lockPair[0])
-                logger.info("Lock trace: %s" % str(lockPair[1]))
-                for lock in lockPair[1].locks:
-                    if not lock.held:
-                        super(LockTestCondition, self).failCheck("Lock detected in a waiting state")
-
-        if super(LockTestCondition, self).getStatus() == 'Inconclusive':
-            super(LockTestCondition, self).passCheck()
-
+        def __lock_info_obtained(result):
+            if (len(self.locks) > 0):
+                """
+                Sometimes, a lock will be held at the end of a test run (typically a logger RDLCK).  Only
+                report a held lock as a failure if the thread is waiting for another lock - that would
+                indicate that we may be in a deadlock situation.  Since that shouldnt happen either
+                before or after a test run, treat that as an error.
+                """
+                for lockPair in self.locks:
+                    logger.info("Detected locks on Asterisk instance: %s" % lockPair[0])
+                    logger.info("Lock trace: %s" % str(lockPair[1]))
+                    for lock in lockPair[1].locks:
+                        if not lock.held:
+                            super(LockTestCondition, self).failCheck("Lock detected in a waiting state")
+
+            if super(LockTestCondition, self).getStatus() == 'Inconclusive':
+                super(LockTestCondition, self).passCheck()
+            self.__finished_deferred.callback(self)
+            return result
+
+        # Build up the locks for each instance of asterisk
+        defer.DeferredList([self.__get_locks(ast) for ast in self.ast]
+                           ).addCallback(__lock_info_obtained)
+        self.__finished_deferred = defer.Deferred()
+        return self.__finished_deferred
 
 class AstMockObjectPassed(object):
     def __init__(self):

Modified: asterisk/trunk/lib/python/asterisk/SipDialogTestCondition.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/SipDialogTestCondition.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/SipDialogTestCondition.py (original)
+++ asterisk/trunk/lib/python/asterisk/SipDialogTestCondition.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2011, Digium, Inc.
+Copyright (C) 2011-2012, Digium, Inc.
 Matt Jordan <mjordan at digium.com>
 
 This program is free software, distributed under the terms of
@@ -14,6 +14,7 @@
 
 from TestConditions import TestCondition
 from starpy import manager
+from twisted.internet import defer
 
 logger = logging.getLogger(__name__)
 
@@ -27,6 +28,8 @@
 
     def __init__(self, test_config):
         super(SipDialogTestCondition, self).__init__(test_config)
+        # a dictionary of ast objects to a dictionary of SIP dialogs and a list of their history
+        self.dialogs_history = {}
 
     def __get_dialog_names(self, objects):
         inObjects = False
@@ -40,19 +43,48 @@
         return dialogNames
 
     def get_sip_dialogs(self, ast):
-        dialogNames = []
-        dialogsHistory = {}
-        objects = ast.cli_exec("sip show objects", True)
-        dialogNames = self.__get_dialog_names(objects)
-
-        for dn in dialogNames:
-            logger.debug("Retrieving history for SIP dialog %s" % dn)
-            rawHistory = ast.cli_exec("sip show history %s" % dn, True)
+        """ Build the dialog history and objects for a particular Asterisk instance """
+        def __show_objects_callback(result):
+            """ Callback for sip show objects """
+            logger.debug(result.output)
+            dialogNames = self.__get_dialog_names(result.output)
+            logger.debug(dialogNames)
+            if not dialogNames:
+                logger.debug("No SIP history found for Asterisk instance %s" % ast.host)
+                self.__finished_deferred.callback(self.__ast)
+                return result
+
+            deferds = []
+            self.__history_requests = []
+            for dn in dialogNames:
+                logger.debug("Retrieving history for SIP dialog %s" % dn)
+                cmd = "sip show history %s" % dn
+                self.__history_requests.append(cmd)
+                deferds.append(ast.cli_exec(cmd).addCallback(__show_history_callback))
+            defer.DeferredList(deferds).addCallback(__history_complete)
+            return result
+
+        def __show_history_callback(result):
+            """ Callback for sip show history """
+            # Get the Call ID from the result
+            call_id = result.cli_cmd.replace("sip show history", "").strip()
+            rawHistory = result.output
+            logger.debug(result.output)
             if 'No such SIP Call ID' not in rawHistory:
-                """ dialog got disposed before we could get its history; ignore """
-                dialogsHistory[dn] = rawHistory.split('\n')
-
-        return dialogsHistory
+                # dialog got disposed before we could get its history; ignore
+                self.dialogs_history[self.__ast.host][call_id] = rawHistory.split('\n')
+            return result
+
+        def __history_complete(result):
+            self.__finished_deferred.callback(self.__ast)
+            return result
+
+        self.__ast = ast
+        self.dialogs_history[self.__ast.host] = {}
+        self.__finished_deferred = defer.Deferred()
+        ast.cli_exec("sip show objects").addCallback(__show_objects_callback)
+
+        return self.__finished_deferred
 
 class SipDialogPreTestCondition(SipDialogTestCondition):
     """
@@ -64,17 +96,38 @@
         super(SipDialogPreTestCondition, self).__init__(test_config)
 
     def evaluate(self, related_test_condition = None):
-        for ast in self.ast:
-            ast.cli_exec("sip set history on")
-            dialogsHistory = super(SipDialogPreTestCondition, self).get_sip_dialogs(ast)
-
+        def __history_finished(result):
+            for ast in self.ast:
+                if ast.host == result.host:
+                    __get_dialogs(ast)
+                    return result
+            logger.warning("Unable to determine Asterisk instance from CLI command run on host %s" % result.host)
+            return result
+
+        def __get_dialogs(ast):
+            super(SipDialogPreTestCondition, self).get_sip_dialogs(ast).addCallback(__dialogs_obtained)
+
+        def __dialogs_obtained(result):
+            dialogsHistory = self.dialogs_history[result.host]
             if len(dialogsHistory) > 0:
-                """ If any dialogs are present before test execution, something funny is going on """
+                # If any dialogs are present before test execution, something funny is going on
                 super(SipDialogPreTestCondition, self).failCheck(
-                    "%d dialogs were detected in Asterisk %s before test execution" % (len(dialogsHistory), ast.host))
+                    "%d dialogs were detected in Asterisk %s before test execution"
+                    % (len(dialogsHistory), result.host))
             else:
                 super(SipDialogPreTestCondition, self).passCheck()
-
+            self.__counter += 1
+            if self.__counter == len(self.ast):
+                # All asterisk instances have been checked
+                self.__finished_deferred.callback(self)
+            return result
+
+        self.__counter = 0
+        self.__finished_deferred = defer.Deferred()
+        # Turn on history and check for dialogs
+        for ast in self.ast:
+            ast.cli_exec("sip set history on").addCallback(__history_finished) 
+        return self.__finished_deferred
 
 class SipDialogPostTestCondition(SipDialogTestCondition):
     """
@@ -97,37 +150,54 @@
             self.sipHistorySequence = test_config.config['sipHistoryRequirements']
 
     def evaluate(self, related_test_condition = None):
-
-        for ast in self.ast:
+        def __get_dialogs():
+            self.__counter += 1
+            if self.__counter == len(self.ast):
+                self.__finished_deferred.callback(self)
+                return
+            super(SipDialogPostTestCondition, self).get_sip_dialogs(
+                self.ast[self.__counter]).addCallback(__dialogs_obtained)
+
+        def __dialogs_obtained(result):
             sipHistoryRequirements = {}
-            dialogsHistory = super(SipDialogPostTestCondition, self).get_sip_dialogs(ast)
-
-            """ Set up the history statements to look for in each dialog history """
+            dialogsHistory = self.dialogs_history[self.ast[self.__counter].host]
+            if not dialogsHistory:
+                __get_dialogs()
+                return result
+
+            # Set up the history statements to look for in each dialog history
             for dialogName in dialogsHistory.keys():
                 sipHistoryCheck = {}
                 for h in self.sipHistorySequence:
                     sipHistoryCheck[h] = False
                 sipHistoryRequirements[dialogName] = sipHistoryCheck
 
-            """ Assume we pass the check.  This will be overriden if any history check fails """
+            # Assume we pass the check.  This will be overriden if any history check fails
             super(SipDialogPostTestCondition, self).passCheck()
-            if (len(dialogsHistory) > 0):
-                for dialog, history in dialogsHistory.items():
-                    scheduled = False
-                    for h in history:
-                        if "SchedDestroy" in h:
-                            scheduled = True
-                        for req in sipHistoryRequirements[dialog].keys():
-                            if req in h:
-                                sipHistoryRequirements[dialog][req] = True
-
-                    if not scheduled:
+            for dialog, history in dialogsHistory.items():
+                scheduled = False
+                for h in history:
+                    if "SchedDestroy" in h:
+                        scheduled = True
+                    for req in sipHistoryRequirements[dialog].keys():
+                        if req in h:
+                            sipHistoryRequirements[dialog][req] = True
+                if not scheduled:
+                    super(SipDialogPostTestCondition, self).failCheck(
+                        "Dialog %s in Asterisk instance %s not scheduled for destruction"
+                        % (dialog, self.ast[self.__counter].host))
+                for req in sipHistoryRequirements[dialog].keys():
+                    if sipHistoryRequirements[dialog][req] == False:
                         super(SipDialogPostTestCondition, self).failCheck(
-                            "Dialog %s in Asterisk instance %s not scheduled for destruction" % (dialog, ast.host))
-                    for req in sipHistoryRequirements[dialog].keys():
-                        if sipHistoryRequirements[dialog][req] == False:
-                            super(SipDialogPostTestCondition, self).failCheck(
-                                "Dialog %s in Asterisk instance %s did not have required step in history: %s" % (dialog, ast.host, req))
+                            "Dialog %s in Asterisk instance %s did not have required step in history: %s"
+                            % (dialog, self.ast[self.__counter].host, req))
+            __get_dialogs()
+            return result
+
+        self.__finished_deferred = defer.Deferred()
+        self.__counter = -1
+        __get_dialogs()
+        return self.__finished_deferred
 
 class TestConfig(object):
     def __init__(self):

Modified: asterisk/trunk/lib/python/asterisk/TestCase.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/TestCase.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/TestCase.py (original)
+++ asterisk/trunk/lib/python/asterisk/TestCase.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2010, Digium, Inc.
+Copyright (C) 2010-2012, Digium, Inc.
 Paul Belanger <pabelanger at digium.com>
 
 This program is free software, distributed under the terms of
@@ -229,8 +229,11 @@
 
         def __perform_pre_checks(result):
             """ Execute the pre-condition checks """
-            self.testConditionController.evaluate_pre_checks()
-            return result
+            df = self.testConditionController.evaluate_pre_checks()
+            if df is None:
+                return result
+            else:
+                return df
 
         def __run_callback(result):
             """ Notify the test that we are running """
@@ -280,24 +283,31 @@
                     # This should already be called when the reactor is being terminated.
                     # If we couldn't stop the instance of Asterisk, there isn't much else to do
                     # here other then complain
+            self.__stop_deferred.callback(self)
             return result
 
-        # Call the overridable method
-        self.stop_asterisk()
-
-        self.testConditionController.evaluate_post_checks()
-
-        # Gather up the stopped defers; check success failure of stopping when
-        # all instances of Asterisk have stopped
-        stop_defers = []
-        for index, item in enumerate(self.ast):
-            logger.info("Stopping Asterisk instance %d" % (index + 1))
-            temp_defer = self.ast[index].stop()
-            stop_defers.append(temp_defer)
-
-        d = defer.DeferredList(stop_defers, consumeErrors=True)
-        d.addCallback(__check_success_failure)
-        return d
+        def __stop_instances(result):
+            # Call the overridable method
+            self.stop_asterisk()
+            # Gather up the stopped defers; check success failure of stopping when
+            # all instances of Asterisk have stopped
+            stop_defers = []
+            for index, item in enumerate(self.ast):
+                logger.info("Stopping Asterisk instance %d" % (index + 1))
+                temp_defer = self.ast[index].stop()
+                stop_defers.append(temp_defer)
+
+            d = defer.DeferredList(stop_defers, consumeErrors=True)
+            d.addCallback(__check_success_failure)
+            return result
+
+        self.__stop_deferred = defer.Deferred()
+        df = self.testConditionController.evaluate_post_checks()
+        if df:
+            df.addCallback(__stop_instances)
+        else:
+            __stop_instances(None)
+        return self.__stop_deferred
 
     def stop_reactor(self):
         """

Modified: asterisk/trunk/lib/python/asterisk/TestConditions.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/TestConditions.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/TestConditions.py (original)
+++ asterisk/trunk/lib/python/asterisk/TestConditions.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2011, Digium, Inc.
+Copyright (C) 2011-2012, Digium, Inc.
 Matt Jordan <mjordan at digium.com>
 
 This program is free software, distributed under the terms of
@@ -13,6 +13,7 @@
 
 from buildoptions import AsteriskBuildOptions
 from TestConfig import TestConfig
+from twisted.internet import defer
 
 logger = logging.getLogger(__name__)
 
@@ -105,33 +106,75 @@
     def evaluate_pre_checks(self):
         """
         Evaluate the pre-test conditions
-        """
-        if (len(self.__prechecks) > 0):
-            logger.debug("Evaluating pre checks")
-            self.__evaluate_checks(self.__prechecks)
+
+        Returns:
+        A deferred that will be raised when all pre-checks have finished,
+        or None if no pre-checks exist
+        """
+        if (not self.__prechecks):
+            return None
+
+        logger.debug("Evaluating pre checks")
+        self.__finished_deferred = defer.Deferred()
+        self.__evaluate_check(self.__prechecks, 0)
+        return self.__finished_deferred
 
     def evaluate_post_checks(self):
         """
         Evaluate the post-test conditions
-        """
-        if (len(self.__postchecks) > 0):
-            time.sleep(10)
-            logger.debug("Evaluating post checks")
-            self.__evaluate_checks(self.__postchecks)
-
-    def __evaluate_checks(self, check_list):
+
+        Returns:
+        A deferred that will be raised when all post-checks have finished,
+        or None if no post-checks exist
+        """
+        if (not self.__postchecks):
+            return None
+
+        logger.debug("Evaluating post checks")
+        self.__finished_deferred = defer.Deferred()
+        self.__evaluate_check(self.__postchecks, 0)
+        return self.__finished_deferred
+
+    def __evaluate_check(self, check_list, counter):
         """ Register the instances of Asterisk and evaluate """
-        for check in check_list:
-            if (check[0].check_build_options()):
-                for ast in self.__ast:
-                    check[0].register_asterisk_instance(ast)
-                if (check[0].getEnabled()):
-                    logger.debug("Evaluating %s" % check[0].getName())
-                    if (check[1] != None):
-                        check[0].evaluate(check[1])
-                    else:
-                        check[0].evaluate()
-                    self.__check_observers(check[0])
+        def __evaluate_callback(result):
+            self.__check_observers(result)
+            self.__check_list_counter += 1
+            self.__evaluate_check(self.__check_list, self.__check_list_counter)
+            return result
+
+        def __evaluate_errback(result):
+            logger.warning("Failed to evaluate condition check %s" % str(result))
+            self.__check_observers(result)
+            self.__check_list_counter += 1
+            self.__evaluate_check(self.__check_list, self.__check_list_counter)
+            return result
+
+        if (counter >= len(check_list)):
+            # All done - raise the finished deferred
+            self.__finished_deferred.callback(self)
+            return
+
+        self.__check_list = check_list
+        self.__check_list_counter = counter
+        # A check object is a tuple of a pre/post condition check, and either
+        # a matching check object used in the evaluation, or None
+        check = check_list[counter]
+
+        # Check to see if the build supports this condition check
+        if not (check[0].check_build_options()):
+            self.__check_list_counter += 1
+            self.__evaluate_check(self.__check_list, self.__check_list_counter)
+
+        for ast in self.__ast:
+            check[0].register_asterisk_instance(ast)
+        if (check[0].getEnabled()):
+            logger.debug("Evaluating %s" % check[0].getName())
+            if (check[1] != None):
+                df = check[0].evaluate(check[1])
+            else:
+                df = check[0].evaluate()
+            df.addCallbacks(__evaluate_callback, __evaluate_errback)
 
     def __check_observers(self, test_condition):
         for observerTuple in self.__observers:
@@ -227,11 +270,15 @@
 
         Derived classes must implement this method and check their test condition
         here.  If the test condition passes they should call passCheck, otherwise they
-        should call failCheck.
+        should call failCheck.  Each test condition must return a twisted deferred, and
+        raise a callback on the deferred when the condition is finished being evaluated.
+        They may raise an errback if a serious error occurs in the evaluation.
 
         Keyword arguments:
         related_test_condition -- A test condition object that is related to this one.  Provided if specified
             when this test condition is registered to the test condition controller.
+        Returns:
+        A twisted deferred object
         """
         pass
 

Modified: asterisk/trunk/lib/python/asterisk/ThreadTestCondition.py
URL: http://svnview.digium.com/svn/testsuite/asterisk/trunk/lib/python/asterisk/ThreadTestCondition.py?view=diff&rev=3188&r1=3187&r2=3188
==============================================================================
--- asterisk/trunk/lib/python/asterisk/ThreadTestCondition.py (original)
+++ asterisk/trunk/lib/python/asterisk/ThreadTestCondition.py Tue Apr 17 18:05:44 2012
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 '''
-Copyright (C) 2011, Digium, Inc.
+Copyright (C) 2011-2012, Digium, Inc.
 Matt Jordan <mjordan at digium.com>
 
 This program is free software, distributed under the terms of
@@ -11,6 +11,7 @@
 import logging.config
 from TestConditions import TestCondition
 from version import AsteriskVersion
+from twisted.internet import defer
 
 logger = logging.getLogger(__name__)
 
@@ -81,16 +82,27 @@
         super(ThreadPreTestCondition, self).__init__(test_config)
 
     def evaluate(self, related_test_condition = None):
-        for ast in self.ast:
-            threads = ast.cli_exec("core show threads", True)
+        """ Override of TestCondition:evaluate """
+        def __show_threads_callback(result):
+            """ Callback from core show threads """
+            threads = result.output
             self.parse_threads(ast, threads)
+            return result
 
-        if len(self.astThreads) > 0:
-            """ All the pre-test cares about is that we saw threads, not what they are """
-            super(ThreadPreTestCondition, self).passCheck()
-        else:
-            super(ThreadPreTestCondition, self).failCheck("No threads found")
+        def __threads_gathered(result):
+            """ Check the results once all threads are gathered """
+            if len(self.astThreads) > 0:
+                # All the pre-test cares about is that we saw threads, not what they are
+                super(ThreadPreTestCondition, self).passCheck()
+            else:
+                super(ThreadPreTestCondition, self).failCheck("No threads found")
+            self.__finished_deferred.callback(self)
+            return result
 
+        self.__finished_deferred = defer.Deferred()
+        defer.DeferredList([ast.cli_exec("core show threads").addCallback(__show_threads_callback)
+                            for ast in self.ast]).addCallback(__threads_gathered)
+        return self.__finished_deferred
 
 class ThreadPostTestCondition(ThreadTestCondition):
     """
@@ -105,58 +117,67 @@
         super(ThreadPostTestCondition, self).__init__(test_config)
 
     def evaluate(self, related_test_condition = None):
+        """ Override of TestCondition:evaluate """
 
-        def evaluateThreadObjInList(threadObj, threadList):
+        def __evaluate_thread_obj_in_list(threadObj, threadList):
             """ Local callback that determines if two thread entries are equal """
             for obj in threadList:
+                # Ignore any remote connections - they may be us
                 if (threadObj[1] == 'netconsole'):
-                    """
-                    Some AMI connections, even after being disconnected, show up in core show threads.  Ignore
-                    netconsole threads for now.
-                    """
                     return True
                 if (threadObj[0] == obj[0] and threadObj[1] == obj[1]):
                     return True
             return False
 
-        """ This must have a related_test_condition value passed in """
+        def __show_threads_callback(result):
+            """ Callback from core show threads """
+            threads = result.output
+            self.parse_threads(ast, threads)
+            return result
+
+        def __threads_gathered(result):
+            """ Called after all core show threads are finished """
+            if not self.astThreads:
+                # No threads found
+                super(ThreadPostTestCondition, self).failCheck("No threads found")
+                self.__finished_deferred.callback(self)
+                return result
+
+            for ast in self.astThreads:
+                # Make sure that for every instance of Asterisk we check in the post-test,
+                # an equivalent instance of Asterisk was checked in the pre-test

[... 71 lines stripped ...]



More information about the asterisk-commits mailing list