<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <base href="https://wiki.asterisk.org/wiki" />
        <style type="text/css">
    body, #email-content, #email-content-inner { font-family: Arial,FreeSans,Helvetica,sans-serif; }
    body, p, blockquote, pre, code, td, th, li, dt, dd { font-size: 13px; }
    small { font-size: 11px; }

    body { width:100% !important; -webkit-font-smoothing: antialiased; }

    body,
    #email-wrapper { background-color: #f0f0f0; }
    #email-wrapper-inner { padding: 20px; text-align: center; }
    #email-content-inner { background-color: #fff; border: 1px solid #bbb; color: $menuTxtColour; padding:20px; text-align:left; }
    #email-wrapper-inner > table { width: 100%; }
    #email-wrapper-inner.thin > table { margin: 0 auto; width: 50%; }
    #email-footer { padding: 0 16px 32px 16px; margin: 0; }

    .email-indent { margin: 8px 0 16px 0; }
    .email-comment { margin: 0 0 0 56px; }
    .email-comment.removed { background-color: #ffe7e7; border: 1px solid #df9898; padding: 0 8px;}

    #email-title-avatar { text-align: left; vertical-align: top; width: 48px; padding-right: 8px; }
    #email-title-flavor { margin: 0; padding: 0 0 4px 0; }
    #email-title-heading { font-size: 16px; line-height: 20px; min-height: 20px; margin: 0; padding: 0; }
    #email-title .icon { border: 0; padding: 0 5px 0 0; text-align: left; vertical-align: middle; }

    #email-actions { border-top: 1px solid #bbb; color: #505050; margin: 8px 0 0 0; padding: 0; }
    #email-actions td { padding-top: 8px; }
    #email-actions .left { max-width: 45%; text-align: left; }
    #email-actions .right { text-align: right; }
    .email-reply-divider { border-top: 1px solid #bbb; color: #505050; margin: 32px 0 8px 0; padding: 8px 0; }
    .email-section-title { border-bottom: 1px solid #bbb; margin: 8px 0; padding: 8px 0 0 0; }

    .email-metadata { color: #505050; }

    a { color: #326ca6; text-decoration: none; }
    a:hover { color: #336ca6; text-decoration: underline; }
    a:active {color: #326ca6; }

    a.email-footer-link { color: #505050; font-size: 11px; }

    .email-item-list { list-style: none; margin: 4px 0; padding-left: 0; }
    .email-item-list li { list-style: none; margin: 0; padding: 4px 0; }
    .email-list-divider { color: #505050; padding: 0 0.35em; }
    .email-operation-icon { padding-right: 5px; }

    .avatar { -ms-interpolation-mode: bicubic; border-radius: 3px;}
    .avatar-link { margin: 2px; }

    .tableview th { border-bottom: 1px solid #69C; font-weight: bold; text-align: left; }
    .tableview td { border-bottom: 1px solid #bbbbbb; text-align: left; padding: 4px 16px 4px 0; }

    .aui-message {  margin: 1em 0; padding: 8px; }
    .aui-message.info { background-color: #e0f0ff; border: 1px solid #9eb6d4; }
    .aui-message.success { background-color: #ddfade; border: 1px solid #93c49f; }
    .aui-message.error,
    .aui-message.removed { background-color: #ffe7e7; border: 1px solid #df9898; color: #000; }

    .call-to-action-table { margin: 10px 1px 1px 1px;}
    .call-to-cancel-container, .call-to-action-container { padding: 5px 20px; }
    .call-to-cancel-container { border: 1px solid #aaa; background-color: #eee; border-radius: 3px; }
    .call-to-cancel-container a.call-to-cancel-button { background-color: #eee; font-size: 14px; line-height: 1; padding: 0; margin: 0; color: #666; font-family: sans-serif;}
    .call-to-action-container { border: 1px solid #486582;  background-color: #3068A2; border-radius: 3px; padding: 4px 10px; }
    .call-to-action-container a.call-to-action-button { background-color: #3068A2; font-size: 14px; line-height: 1; padding: 0; margin: 0; color: #fff; font-weight: bold; font-family: sans-serif; }

    /** The span around the inline task checkbox image */
    .diff-inline-task-overlay {
        display: inline-block;
        text-align: center;
        height: 1.5em;
        padding: 5px 0px 1px 5px;
        margin-right: 5px;
        /** Unfortunately, the negative margin-left is stripped out in gmail */
        margin-left: -5px;
    }

            @media handheld, only screen and (max-device-width: 480px) {
        div, a, p, td, th, li, dt, dd { -webkit-text-size-adjust: auto; }
        small, small a { -webkit-text-size-adjust: 90%; }

        td[id=email-wrapper-inner] { padding: 2px !important; }
        td[id=email-content-inner] { padding: 8px !important; }
        td[id="email-wrapper-inner"][class="thin"] > table { text-align: left !important; width: 100% !important; }
        td[id=email-footer] { padding: 8px 12px !important; }
        div[class=email-indent] { margin: 8px 0px !important; }
        div[class=email-comment] { margin: 0 !important; }

        p[id=email-title-flavor] a { display: block; } /* puts the username and the action on separate lines */
        p[id=email-permalink] { padding: 4px 0 0 0 !important; }

        table[id=email-actions] td { padding-top: 0 !important; }
        table[id=email-actions] td.right { text-align: right !important; }
        table[id=email-actions] .email-list-item { display: block; margin: 1em 0 !important; word-wrap: normal !important; }
        span[class=email-list-divider] { display: none; }
    }



        </style>
    </head>
    <body style="font-family: Arial, FreeSans, Helvetica, sans-serif; font-size: 13px; width: 100%; -webkit-font-smoothing: antialiased; background-color: #f0f0f0">
        <table id="email-wrapper" width="100%" cellspacing="0" cellpadding="0" border="0" style="background-color: #f0f0f0">
            <tbody>
                <tr valign="middle">
                    <td id="email-wrapper-inner" style="font-size: 13px; padding: 20px; text-align: center">
                        <table id="email-content" cellspacing="0" cellpadding="0" border="0" style="font-family: Arial, FreeSans, Helvetica, sans-serif; width: 100%">
                            <tbody>
                                <tr valign="top">
                                    <td id="email-content-inner" align="left" style="font-family: Arial, FreeSans, Helvetica, sans-serif; font-size: 13px; background-color: #fff; border: 1px solid #bbb; padding: 20px; text-align: left">
                                        <table id="email-title" cellpadding="0" cellspacing="0" border="0" width="100%">
                                            <tbody>
                                                <tr>
                                                    <td id="email-title-avatar" rowspan="2" style="font-size: 13px; text-align: left; vertical-align: top; width: 48px; padding-right: 8px"> <img class="avatar" src="cid:avatar_ce51dcf276530e4a4b00548e2a6d0905" border="0" height="48" width="48" style="-ms-interpolation-mode: bicubic; border-radius: 3px" /> </td>
                                                    <td valign="top" style="font-size: 13px">
                                                        <div id="email-title-flavor" class="email-metadata" style="margin: 0; padding: 0 0 4px 0; color: #505050">
                                                            <a href="    https://wiki.asterisk.org/wiki/display/~mjordan " style="color:#326ca6;text-decoration:none;; color: #326ca6; text-decoration: none">Matt Jordan</a> edited the page:
                                                        </div> </td>
                                                </tr>
                                                <tr>
                                                    <td valign="top" style="font-size: 13px"> <h2 id="email-title-heading" style="font-size: 16px; line-height: 20px; min-height: 20px; margin: 0; padding: 0"> <a href="https://wiki.asterisk.org/wiki/display/AST/Pluggable+Component+Framework" style="color: #326ca6; text-decoration: none"> <img class="icon" src="cid:page-icon" alt="" style="border: 0; padding: 0 5px 0 0; text-align: left; vertical-align: middle" /> <strong style="font-size:16px;line-height:20px;vertical-align:top;">Pluggable Component Framework</strong> </a> </h2> </td>
                                                </tr>
                                            </tbody>
                                        </table>
                                        <div class="email-indent" style="margin: 8px 0 16px 0">
                                            <div class="email-diff">
                                                <div id="page-diffs" class="wiki-content">
                                                    <h1 id="PluggableComponentFramework-Motivation" class="diff-block-target diff-block-context"> <span class="diff-html-added" id="added-diff-0" style="font-size: 100%; background-color: #ddfade;">Motivation</span> </h1>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">At its heart, the Asterisk Test Suite is a way of executing programs that test functionality in Asterisk. Historically, the Test Suite has implemented these tests in a variety of languages - Lua, bash, Python - but there is no hard rule that limits what is supported. If your implementation supports having an executable file named </span><em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">run-test</span></em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">, then it is acceptable by the Test Suite.</span> </p>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">There are some problems that arise with this level of flexibility. Tests often have to do many of the same tasks: start and stop Asterisk, connect over AMI and originate calls, run AGI scripts, gather and report results, etc. For each language chosen, sets of libraries have grown to support these common activities. This has resulted in duplication of functionality between libraries written in different languages. Over time, some libraries have also received more attention than others as well, resulting in functionality that is only available in a particular language.</span> </p>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">In the current state of the Asterisk Test Suite, we've focused our efforts on writing our tests in Python. There is no strict enforcement of this, but since we have developed a number of libraries to support test execution - and continue to expand and enhance those libraries - you may get more "bang for your buck" by writing tests in Python as well.</span> </p>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">All of that said, even when a single implementation language is chosen, there is still a fair amount of repeated code throughout the Test Suite. Because tests support their own execution, they often have to implement similar mechanisms. Each test must have its own execution entry point, instantiate its own objects, do the minimum amount needed to get Asterisk running, etc. Between tests that cover similar functionality, there is even more repeated code. Moving shared code into libraries helps alleviate that to some extent, but even then some amount of duplication occurs.</span> </p>
                                                    <h1 id="PluggableComponentFramework-AnExample-TwoTraditionalTestsUsingSIPp" class="diff-block-target diff-block-context"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">An Example - Two Traditional Tests Using SIPp</span> </h1>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">As an example, consider two tests written in Python that run SIPp scenarios - </span><strong><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">message_auth</span></strong><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> and </span><strong><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">message_disabled</span></strong><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">.</span> </p>
                                                    <h5 id="PluggableComponentFramework-message_auth" class="diff-block-target diff-block-context"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">message_auth</span> </h5>
                                                    <table class="diff-macro diff-html-added diff-block-target diff-block-context" style="background-color: #f0f0f0;border: 1px solid #dddddd;margin: 10px 1px;padding: 0 2px 2px;width: 100%;background-color: #ddfade;border-color: #93c49f;">
                                                        <thead>
                                                            <tr>
                                                                <th class="diff-macro-title" style="background-color: transparent; text-align: left; font-weight: normal;padding: 5px;; font-size: 13px"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"><span class="icon macro-placeholder-icon" style="background-color: ;line-height: 20px;"><img src="https://wiki.asterisk.org/wiki/s/en_GB-1988229788/4252/6ac85e9b14675c5514a674e1aecae99c9505ed36.48/_/plugins/servlet/confluence/placeholder/macro-icon?name=code" style="padding-right: 5px; vertical-align: text-bottom;" /> </span>Code Block</span></th>
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            <tr>
                                                                <td class="diff-macro-body" style="background-color: #fff;border: 1px solid #dddddd;padding: 10px;; font-size: 13px"> <pre style="font-size: 13px">
<span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">#!/usr/bin/env python
'''
Copyright (C) 2010, Digium, Inc.
Russell Bryant <russell@digium.com>

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

import sys
import os

sys.path.append("lib/python")

from twisted.internet import reactor
from asterisk.sipp import SIPpTest


WORKING_DIR = "sip/message_auth"
TEST_DIR = os.path.dirname(os.path.realpath(__file__))

SIPP_SCENARIOS = [
    {
        'scenario' : 'message_recv.xml',
        '-p' : '5062'
    },
    {
        'scenario' : 'message.xml',
        '-p' : '5061'
    }
]


def main():
    test = SIPpTest(WORKING_DIR, TEST_DIR, SIPP_SCENARIOS)
    reactor.run()
    if not test.passed:
        return 1

    return 0

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

</span>
</pre> </td>
                                                            </tr>
                                                        </tbody>
                                                    </table>
                                                    <h5 id="PluggableComponentFramework-message_disabled" class="diff-block-target diff-block-context"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">message_disabled</span> </h5>
                                                    <table class="diff-macro diff-html-added diff-block-target diff-block-context" style="background-color: #f0f0f0;border: 1px solid #dddddd;margin: 10px 1px;padding: 0 2px 2px;width: 100%;background-color: #ddfade;border-color: #93c49f;">
                                                        <thead>
                                                            <tr>
                                                                <th class="diff-macro-title" style="background-color: transparent; text-align: left; font-weight: normal;padding: 5px;; font-size: 13px"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"><span class="icon macro-placeholder-icon" style="background-color: ;line-height: 20px;"><img src="https://wiki.asterisk.org/wiki/s/en_GB-1988229788/4252/6ac85e9b14675c5514a674e1aecae99c9505ed36.48/_/plugins/servlet/confluence/placeholder/macro-icon?name=code" style="padding-right: 5px; vertical-align: text-bottom;" /> </span>Code Block</span></th>
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            <tr>
                                                                <td class="diff-macro-body" style="background-color: #fff;border: 1px solid #dddddd;padding: 10px;; font-size: 13px"> <pre style="font-size: 13px">
<span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">#!/usr/bin/env python
'''
Copyright (C) 2010, Digium, Inc.
Russell Bryant <russell@digium.com>

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

import sys
import os

sys.path.append("lib/python")

from twisted.internet import reactor
from asterisk.sipp import SIPpTest


WORKING_DIR = "sip/message_disabled"
TEST_DIR = os.path.dirname(os.path.realpath(__file__))

SIPP_SCENARIOS = [
    {
        'scenario' : 'message.xml',
    }
]


def main():
    test = SIPpTest(WORKING_DIR, TEST_DIR, SIPP_SCENARIOS)
    reactor.run()
    if not test.passed:
        return 1

    return 0


if __name__ == "__main__":
    sys.exit(main())
</span>
</pre> </td>
                                                            </tr>
                                                        </tbody>
                                                    </table>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Both of these are very straight-forward tests that simply execute their SIPp scenarios and base their pass/fail status on the result of those scenarios. Even still, there is repeated code:</span> </p>
                                                    <ul class="diff-block-target diff-block-context">
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Duplicate entry points for execution</span> </li>
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Manipulation of the </span><a href="http://twistedmatrix.com/trac/" class="external-link" rel="nofollow" style="color: #326ca6; text-decoration: none"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">twisted</span></a><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> reactor</span> </li>
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Instantiation of the test object, and reading of its result</span> </li>
                                                    </ul>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">In fact, if we look at what is unique, it consists only of the SIPp scenarios to run and the parameters passed to SIPp. Even the working and test directories can be inferred by higher level entities, or specified via configuration. If the only differences between tests can be expressed by configuration, why not do that?</span> </p>
                                                    <h1 id="PluggableComponentFramework-APluggableFrameworkImplementation" class="diff-block-target diff-block-context"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">A Pluggable Framework Implementation</span> </h1>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">These observations led to the development of a pluggable component framework for the Asterisk Test Suite. Tests that support this framework specify what components they need and their configuration in their </span><em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">test-config.yaml</span></em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> configuration files. A Python module, </span><em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">TestRunner.py</span></em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">, is responsible for starting execution, instantiating the components and injecting their dependencies, starting the test, and reporting results. The actual "business logic" of the test itself is deferred to the components specified in the test configuration.</span> </p>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Looking again at the two tests using SIPp, we could discard both of their </span><em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">run-test</span></em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> script files, and instead express all of their intent simply in their </span><em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">test-config.yaml</span></em><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> configuration files. Assuming the SIPpTest class was modified slightly to be able to parse the scenario information from the </span><a class="confluence-link unresolved" href="#" style="color: #326ca6; text-decoration: none"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">YAML</span></a><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> file, one particular implementation of this could express those tests in the following manner:</span> </p>
                                                    <h5 id="PluggableComponentFramework-message_auth.1" class="diff-block-target diff-block-context"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">message_auth</span> </h5>
                                                    <table class="diff-macro diff-html-added diff-block-target diff-block-context" style="background-color: #f0f0f0;border: 1px solid #dddddd;margin: 10px 1px;padding: 0 2px 2px;width: 100%;background-color: #ddfade;border-color: #93c49f;">
                                                        <thead>
                                                            <tr>
                                                                <th class="diff-macro-title" style="background-color: transparent; text-align: left; font-weight: normal;padding: 5px;; font-size: 13px"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"><span class="icon macro-placeholder-icon" style="background-color: ;line-height: 20px;"><img src="https://wiki.asterisk.org/wiki/s/en_GB-1988229788/4252/6ac85e9b14675c5514a674e1aecae99c9505ed36.48/_/plugins/servlet/confluence/placeholder/macro-icon?name=code" style="padding-right: 5px; vertical-align: text-bottom;" /> </span>Code Block</span></th>
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            <tr>
                                                                <td class="diff-macro-body" style="background-color: #fff;border: 1px solid #dddddd;padding: 10px;; font-size: 13px"> <pre style="font-size: 13px">
<span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">testinfo:
    summary: 'Test response to MESSAGE when out-of-call MESSAGE is disabled (or not supported)'
    description: |
        'Send Asterisk a MESSAGE and expect that it gets denied'

test-modules:
    test-object:
        config-section: test-object-config
        typename: 'SIPpTest.SIPpTest'

test-object-config:
    pass-on-sipp-scenario: True
    scenarios:
        -
            scenario: 'message_recv.xml'
            -p: '5062'
        -
            scenario: 'message.xml'
            -p: '5061'
</span>
</pre> </td>
                                                            </tr>
                                                        </tbody>
                                                    </table>
                                                    <h5 id="PluggableComponentFramework-message_disabled.1" class="diff-block-target diff-block-context"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">message_disabled</span> </h5>
                                                    <table class="diff-macro diff-html-added diff-block-target diff-block-context" style="background-color: #f0f0f0;border: 1px solid #dddddd;margin: 10px 1px;padding: 0 2px 2px;width: 100%;background-color: #ddfade;border-color: #93c49f;">
                                                        <thead>
                                                            <tr>
                                                                <th class="diff-macro-title" style="background-color: transparent; text-align: left; font-weight: normal;padding: 5px;; font-size: 13px"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"><span class="icon macro-placeholder-icon" style="background-color: ;line-height: 20px;"><img src="https://wiki.asterisk.org/wiki/s/en_GB-1988229788/4252/6ac85e9b14675c5514a674e1aecae99c9505ed36.48/_/plugins/servlet/confluence/placeholder/macro-icon?name=code" style="padding-right: 5px; vertical-align: text-bottom;" /> </span>Code Block</span></th>
                                                            </tr>
                                                        </thead>
                                                        <tbody>
                                                            <tr>
                                                                <td class="diff-macro-body" style="background-color: #fff;border: 1px solid #dddddd;padding: 10px;; font-size: 13px"> <pre style="font-size: 13px">
<span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">testinfo:
    summary: 'Test response to MESSAGE when out-of-call MESSAGE is disabled (or not supported)'
    description: |
        'Send Asterisk a MESSAGE and expect that it gets denied'

test-modules:
    test-object:
        config-section: test-object-config
        typename: 'SIPpTest.SIPpTest'

test-object-config:
    pass-on-sipp-scenario: True
    scenarios:
        -
            scenario: 'message.xml'

properties:
    minversion: '1.8.0.0'
    dependencies:
        - app : 'sipp'
    tags:
        - SIP
</span>
</pre> </td>
                                                            </tr>
                                                        </tbody>
                                                    </table>
                                                    <p class="diff-block-target diff-block-context" style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">All of this, hopefully, gives the following benefits:</span> </p>
                                                    <ul class="diff-block-target diff-block-context">
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Less time to write a test, particularly if you have a large number of tests that cover related functionality, or your tests can leverage existing Python libraries</span> </li>
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Some more difficult concepts - such as asynchronous event programming using twisted - may be deferred to lower level libraries (bad pun)</span> </li>
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Quicker execution time, by taking advantage of byte code files compiled by the Python interpreter</span> </li>
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Shared functionality across tests</span> </li>
                                                        <li style="font-size: 13px"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Common implementation of similar functionality across tests, easing test maintenance</span> </li>
                                                    </ul>
                                                </div>
                                            </div>
                                        </div>
                                        <table id="email-actions" class="email-metadata" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-top: 1px solid #bbb; color: #505050; margin: 8px 0 0 0; padding: 0; color: #505050">
                                            <tbody>
                                                <tr>
                                                    <td class="left" valign="top" style="font-size: 13px; padding-top: 8px; max-width: 45%; text-align: left"> <span class="email-list-item"><a href="https://wiki.asterisk.org/wiki/display/AST/Pluggable+Component+Framework" style="color: #326ca6; text-decoration: none">View Online</a> </span> <span class="email-list-divider" style="color: #505050; padding: 0 0.350em">·</span> <span class="email-list-item"><a href="https://wiki.asterisk.org/wiki/plugins/likes/like.action?contentId=20185570" style="color: #326ca6; text-decoration: none">Like</a> </span> <span class="email-list-divider" style="color: #505050; padding: 0 0.350em">·</span> <span class="email-list-item"><a href="https://wiki.asterisk.org/wiki/pages/diffpagesbyversion.action?pageId=20185570&revisedVersion=3&originalVersion=2" style="color: #326ca6; text-decoration: none">View Changes</a> </span> <span class="email-list-divider" style="color: #505050; padding: 0 0.350em">·</span> <span class="email-list-item"><a href="https://wiki.asterisk.org/wiki/display/AST/Pluggable+Component+Framework?showComments=true&showCommentArea=true#addcomment" style="color: #326ca6; text-decoration: none">Add Comment</a> </span> </td>
                                                    <td class="right" width="50%" valign="top" style="font-size: 13px; padding-top: 8px; text-align: right"> <span class="email-list-item"><a href="https://wiki.asterisk.org/wiki/users/removespacenotification.action?spaceKey=AST" style="color: #326ca6; text-decoration: none">Stop watching space</a> </span> <span class="email-list-divider" style="color: #505050; padding: 0 0.350em">·</span> <span class="email-list-item"><a href="https://wiki.asterisk.org/wiki/users/editmyemailsettings.action" style="color: #326ca6; text-decoration: none">Manage Notifications</a> </span> </td>
                                                </tr>
                                            </tbody>
                                        </table> </td>
                                </tr>
                            </tbody>
                        </table> </td>
                </tr>
                <tr>
                    <td id="email-footer" align="center" style="font-size: 13px; padding: 0 16px 32px 16px; margin: 0"> <small style="font-size: 11px"> This message was sent by <a class="email-footer-link" style="color:#505050;font-size:11px;text-decoration:none;; color: #326ca6; text-decoration: none; color: #505050; font-size: 11px" href="http://www.atlassian.com/software/confluence">Atlassian Confluence</a> 5.1.5, <a class="email-footer-link" style="color:#505050;font-size:11px;text-decoration:none;; color: #326ca6; text-decoration: none; color: #505050; font-size: 11px" href="http://www.atlassian.com/software/confluence/overview/team-collaboration-software?utm_source=email-footer">Team Collaboration Software</a> </small> </td>
                </tr>
            </tbody>
        </table>
    </body>
</html>