#!/usr/bin/env python
# vim: sw=3 et:
"""The skeleton of a Python test

Copyright (C) 2011, Digium, Inc.
Matt Jordan <mjordan@digium.com>

This program is free software, distributed under the terms of
the GNU General Public License Version 2.
"""

import sys
import logging

# The tests generally use the twisted reactor (an asynchronous event driven
# engine) to orchestrate actions to take during test execution.
from twisted.internet import reactor

# Include the location of our python libraries, relative to the execution
# directory (always assumed to be where the top-most runtests.py resides)
sys.path.append("lib/python")

# Imports from the TestSuite library.  While it is not needed, it is
# recommended to at least import and derive from the TestCase class,
# which provides common functionality to the various tests
from asterisk.test_case import TestCase

# The TestCase class will initialize the python logger - creating a
# logger here will log under the '__main__' namespace
LOGGER = logging.getLogger(__name__)


class YourTestName(TestCase):
    """A class that executes your test.
    
    In general, this class should derive from test_case.TestCase. This minimizes
    the amount of code duplication and provides a significant amount of common
    functionality between tests, including:
      - Asterisk instance management
      - AMI management
      - FastAGI management
      - Logging initialization
      - Automatic Pre- and Post-Test condition checking
      - Management and control of Asterisk AMI TestEvent notifications
    """

    def __init__(self):
        super(YourTestName, self).__init__()
        """You should always call the base class implementation of __init__
        prior to initializing your test conditions here. Some useful variables
        the TestCase class provides:
         - passed - set to False initially. Set to True if your test passes.
                    Typically, you should use TestCase.set_passed(Bool) to
                    modify this value.
         - ast - a list of Asterisk instance
         - ami - a list of StarPY manager (AMI) instances, corresponding to
                 each Asterisk instance
         - reactor_timeout - maximum time a test can execute before it is
                 considered to fail. This prevents tests from hanging and never
                 finishing. You can reset this timer using a call to
                 TestCase.reset_timeout()

        In your initialization, you should usually set the reactor_timeout if
        it should be something other than 30 (the default). In your constructor,
        you should also call create_asterisk, which will create and initialize
        the Asterisk instances. You can specify as a parameter the number of
        Asterisk instances to create.
        """
        # self.reactor_timeout = 90
        self.create_asterisk(count=1)

    def run(self):
        """Run is called by the reactor when the test starts. It is the entry
        point for test execution, and will occur after Asterisk is started (so
        long as the instructions in this example are followed). Typically, after
        calling the base class implementation, connections over AMI are created.
        You can also interact with the started Asterisk instances here by
        referencing the ast list.
        """
        super(YourTestName, self).run()

        # Create a connection over AMI to the created Asterisk instance. If you
        # need to communicate with all of the instances of Asterisk that were
        # created, specify the number of AMI connections to make. When the AMI
        # connection succeeds, ami_connect will be called.
        self.create_ami_factory(count=1)

    def ami_connect(self, ami):
        """This method is called by the StarPY manager class when AMI connects
        to Asterisk.  

        Keyword Arguments:
        ami  The StarPY manager object that connected

        This same method is used for all AMI connections - so to tell which AMI
        connection you are receiving, check the ami.id property. Each AMI
        connection corresponds exactly to the instance of Asterisk in the ast
        list - so ast[ami.id] will reference the Asterisk instance associated
        with the ami object.
        """

        # Very often, tests will indicate their pass / fail status by sending a
        # UserEvent. You can register a callback for these events here
        ami.registerEvent('UserEvent', self.user_event)

        # Originate a call to a dummy extension.  Note that this extension is
        # defined in ast1/configs/extensions.conf - which is automatically
        # installed for us when we called create_asterisk
        deferred = ami.originate(channel="Local/1@default", application="Echo")

        # The TestCase class has a handler for originate failures that will
        # automatically fail the test. If you don't expect the originate to
        # fail, use this callback to catch issues in Asterisk that can occur
        # when originates fail
        deferred.addErrback(self.handle_originate_failure)

    def user_event(self, ami, event):
        """Callback handler for a UserEvent received over AMI.

        Note that we registered this callback handler in ami_connect.

        Keyword Arguments:
        ami    The instance of AMI that received this event
        event  The AMI event object
        """

        if event['userevent'] != 'TestResult':
            # It's always a good idea to check if your UserEvent is the one you
            # want. Note that all keys in the event dictionary object will be
            # lowercase.
            return

        if event['result'] == "pass":
            self.set_passed(True)
            LOGGER.info("Test successfully exited")
        else:
            LOGGER.warn("Test did not successfully exit")
            self.set_passed(False)

        
        # TestCase.stop_reactor will automatically stop the current test. In
        # this case, we stop the test on the first UserEvent we receive, and set
        # the pass / fail status of the test based on the parameters in that
        # UserEvent. More complicated tests could use the AMI object passed in
        # to originate another call, interact with Asterisk, etc. and continue
        # processing until some condition was met before stopping.
        #
        # Stopping the reactor automatically stops all instances of Asterisk
        # in as graceful a manner as possible
        self.stop_reactor()


def main():
    """Main entry point for the test. All run-test scripts execute with a main,
    and should consist of the following steps:
    1. Instantiate the test object
    2. Run the twisted reactor.  This will automatically do the following:
       - Start the Asterisk instances
       - Call the test's run method when twisted is up
    3. When the twisted reactor exits, check results.  If the test passed,
       return 0; otherwise, return any other value (usually 1)
    """

    test = YourTestName()
    reactor.run()

    if not test.passed:
        return 1

    return 0

if __name__ == "__main__":
    sys.exit(main() or 0)
