##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################

USAGE = '''\
%(program)s -- Test script for DOM implementations.

usage: %(program)s [-h] [implementations...]

Parameters:
    -g      Use the Tkinter GUI from unittestgui, if available.

    -h      Print this help text.

    -x      Treat the list of tests as tests to exclude rather than include.

    tests   List of tests to execute.  By default, all known testcases are run.
            Known tests include:

            - DOM          the basic DOM
            - ParsedXML    the persistent/wrapped Zope DOM
            - Printer      the DOM-to-XML Printer
            - Parser       the Expat parser and DOM builder
            - Persistence  persistence of the managed DOM
            - Truthable    checks for truth tests
            - Acquisition  checks for acquisition-related integrity failure
'''

import getopt
import os
import sys
import imp
import unittest
import Products.ParsedXML

impls = (
    ('DOM',             'test_dom'),
    ('ParsedXML',       'test_wrappeddom'),
    ('Printer',         'test_printer'),
    ('Parser',          'test_parser'),
    ('Persistence',     'test_persistence'),
    ('Truthable',       'test_truthable'),
    ('UserAcquisition', 'test_acquisition'),
    # known parenting bug, also causes errors in DOM tests
    ('Acquisition',     'test_aqpain'),
    ('ZopeInterface',   'test_zopeinterface'),
# FIXME: this is causing a segfault with debian's python 2.1, though
# not with a manually compiled python.. need to look into this 
#    ('Refcounts',       'test_collection'),
    ('ODB',             'test_ODB'),
    ('elementId',       'test_elementid'),
    )

def main():
    program = os.path.basename(sys.argv[0])
    impls_to_exclude = None
    gui = 0
    try:
        opts, impls_to_test = getopt.getopt(
            sys.argv[1:], "dghx", ["disable-gc", "gui", "help", "exclude"])
    except getopt.error:
        sys.stdout = sys.stderr
        print USAGE % {"program": program}
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-x", "--exclude"):
            impls_to_exclude = impls_to_test
            impls_to_test = []
        elif opt in ("-d", "--disable-gc"):
            try:
                import gc
            except ImportError:
                # nothing to disable
                pass
            else:
                gc.disable()
        elif opt in ("-g", "--gui"):
            gui = 1
        elif opt in ("-h", "--help"):
            print USAGE % {"program": program}
            sys.exit()

    # Make a backup, so you can see differences between runs.
    if os.path.exists('out.txt'):
        if os.path.exists('out.txt.bak'):
            os.unlink('out.txt.bak')
        os.rename('out.txt', 'out.txt.bak')
    
    if gui:
        run_tests = run_tests_gui
    else:
        run_tests = run_tests_text

    if run_tests(impls_to_test, impls_to_exclude):
        sys.exit(1)


def run_tests_gui(include, exclude):
    try:
        import unittestgui
    except ImportError:
        sys.stderr.write(
            "unittestgui module not installed; install that module from\n"
            "PyUNIT and try again.\n")
        return 1
    else:
        unittestgui.main("__main__.all_suites")
        return 0


def run_tests_text(include, exclude):
    errs = 0
    outf = open("out.txt", "w")
    stdout = sys.stdout

    for implname, filename in impls:
        if include and implname not in include:
            continue
        elif exclude and implname in exclude:
            continue

        sys.stdout = outf
        print "===== %s =====\n" % implname

        try:
            # Import test file and retrieve test suite.
            testSuite = load_suite(filename)
            errs = errs + run_suite(testSuite, implname, outf)
        finally:
            sys.stdout = stdout

    outf.close()
    return errs


def load_suite(filename):
    path = os.path.join(sys.modules['Products.ParsedXML'].__path__[0],
                        'tests')
    file, pathname, desc = imp.find_module(filename, [path])
    module = imp.load_module(filename, file, pathname, desc)
    file.close()
    return module.test_suite()


def all_suites():
    suite = unittest.TestSuite()
    for name, module in impls:
        suite.addTest(load_suite(module))
    return suite


def run_suite(testSuite, implname, outf=None):
    if outf is None:
        outf = sys.stdout
    if hasattr(unittest, 'JUnitTextTestRunner'):
        # Prefer the non-verbose version.
        runner = unittest.JUnitTextTestRunner(outf)
    else:
        runner = unittest.TextTestRunner(outf)

    result = runner.run(testSuite)

    print "\n\n"
    if result.errors:
        print "Errors:"
        map(print_error, result.errors)
        print
    if result.failures:
        print "Failures:"
        map(print_error, result.failures)
        print
    newerrs = len(result.errors) + len(result.failures)
    if newerrs:
        sys.stderr.write("%s: %d errors, %d failures\n"
                         % (implname, len(result.errors),
                            len(result.failures)))
    return newerrs


def print_error(info):
    testcase, (type, e, tb) = info
    print "  %s.%s.%s" % (testcase.__class__.__module__,
                          testcase.__class__.__name__,
                          tb.tb_frame.f_code.co_name)


if __name__ == "__main__":
    main()
