#                Perforce Defect Tracking Integration Project
#                 <http://www.ravenbrook.com/project/p4dti/>
#
#              TEST_MESSAGE.PY -- UNIT TESTS FOR MESSAGE MODULE
#
#                 Gareth Rees, Ravenbrook Limited, 2001-03-13
#
#
# 1. INTRODUCTION
#
# This module defines a set of unit tests for the message module.
#
# It uses the PyUnit unit test framework [PyUnit].
#
# The intended readership is project developers.
#
# This document is not confidential.

import os
import sys
p4dti_path = os.path.join(os.getcwd(), os.pardir, 'code', 'replicator')
if p4dti_path not in sys.path:
    sys.path.append(p4dti_path)
import message
import p4dti_unittest
import string
import unittest
import whrandom


# 2. TEST CASES


# 2.1. Message creation and conversion
#
# Test that messages can be created and converted to a string.

success_cases = [
    ( 1, "Test 1", message.DEBUG, "Foo", "(Foo-11)  Test 1" ),
    ( 22, "Test 2", message.INFO, "XYZ", "(XYZ-226)  Test 2" ),
    ( 333, "Test 3", message.NOTICE, "", "(3337)  Test 3" ),
    ( 4444, "Test 4", message.WARNING, "P4DTI", "(P4DTI-44447)  Test 4" ),
    ( 12345, "Test 5", message.ERR, "%%%%", "(%%%%-123452)  Test 5" ),
    ( 9999, "Test 6", message.CRIT, "%s%d", "(%s%d-99992)  Test 6" ),
    ( 999, "Test 7", message.ALERT, "abcdefg", "(abcdefg-999X)  Test 7" ),
    ( 0, "Test 8", message.EMERG, "", "(00)  Test 8" ),
    ]

class create(p4dti_unittest.TestCase):
    def runTest(self):
        "Message creation and conversion (test_message.create)"
        for case in success_cases:
            (id, text, priority, product, expected) = case
            msg = message.message(id, text, priority, product)
            found = str(msg)
            if expected != found:
                self.addFailure("Case %s: expected '%s' but found '%s'."
                                % (case, expected, found))


# 2.2. Failure on message creation
#
# Test that attempts to create messages with bogus data fail.

failure_cases = [
    ( None, "Failure 1", message.EMERG, "Foo", AssertionError ),
    ( -1, "Failure 2", message.INFO, "XYZ", AssertionError ),
    ( 0, None, message.EMERG, "", AssertionError ),
    ( 999, "Failure 4", "message.CRIT", "Foo", AssertionError ),
    ( 56, "Failure 5", -1, "P4DTI", AssertionError ),
    ( 12345, "Failure 6", 100, "abcd", AssertionError ),
    ( 1234, "Failure 7", message.EMERG, 999, AssertionError ),
    ]

class fail(p4dti_unittest.TestCase):
    def runTest(self):
        "Creating messages with bogus data fails (test_message.fail)"
        for case in failure_cases:
            (id, text, priority, product, expected) = case
            try:
                msg = message.message(id, text, priority, product)
            except expected:
                pass
            else:
                self.addFailure("Case %s: expected %s." % (case, expected))


# 2.3. Message wrapping
#
# Test that wrapping to a number of columns works as expected.

class wrap(p4dti_unittest.TestCase):
    def runTest(self):
        "Message wrapping (test_message.wrap)"
        # On trial 0, try cases where no word is longer than the number of
        # columns.  On trial 1, try cases where some words are longer than the
        # number of columns.
        for trial in range(2):
            columns = 1
            while columns < 1000:
                nwords = 10 + whrandom.randint(0, columns)
                def word(_, c=columns, t=trial):
                    return (string.letters[whrandom.randint(0,25)]
                            * whrandom.randint(1, c * (t + 1)))
                text = string.join(map(word, range(nwords)), ' ')
                msg = message.message(0, text, message.DEBUG, "")
                expected = str(msg)
                wrapped = msg.wrap(columns)
                lines = string.split(wrapped, '\n')
                if lines[0] == '(00)':
                    found = lines[0] + '  ' + string.join(lines[1:], ' ')
                else:
                    found = string.join(lines, ' ')
                for l in range(0,len(lines)):
                    # On trial 0 each line must fit in the columns, since no
                    # word was longer than columns.
                    if (trial == 0 and (l > 0 or columns > 4)
                        and len(lines[l]) > columns):
                        self.addFailure("Wrapping to %d columns, line %d has "
                                        "length %d (%s)."
                                        % (columns, l, len(lines[l]), lines))
                    # Lines longer than the columns consist of a single word.
                    if (len(lines[l]) > columns
                        and string.find(lines[l], ' ') != -1):
                        self.addFailure("Wrapping to %d columns, line %d has "
                                        "a space (%s)." % (columns, l, lines))
                    # No line can be extended by adjoining the first word from
                    # the following line without making it go over the number
                    # of columns.
                    if (l + 1 < len(lines)
                        and (len(lines[l]) + 1 + (l == 0) +
                             len(string.split(lines[l+1],' ')[0])) <= columns):
                        self.addFailure("Wrapping to %d columns, line %d "
                                        "could have an extra word in it (%s)."
                                        % (columns, l, lines))
                if expected != found:
                    self.addFailure("Wrapping to %d columns:\nExpected: %s\n"
                                    "Found: %s\nWrapped: %s."
                                    % (columns, expected, found, `wrapped`))
                columns = columns * 2 + whrandom.randint(0,1)


# 3. RUNNING THE TESTS

def tests():
    suite = unittest.TestSuite()
    for t in [create, fail, wrap]:
        suite.addTest(t())
    return suite

if __name__ == "__main__":
    unittest.main(defaultTest="tests")


# A. REFERENCES
#
# [PyUnit] "PyUnit - a unit testing framework for Python"; Steve Purcell;
# <http://pyunit.sourceforge.net/>.
#
#
# B. DOCUMENT HISTORY
#
# 2001-03-13 GDR Created.
#
# 2001-04-24 GDR Use p4dti_unittest to collect many failures per test case.
#
#
# C. COPYRIGHT AND LICENCE
#
# This file is copyright (c) 2001 Perforce Software, Inc.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1.  Redistributions of 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.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS 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 THE COPYRIGHT HOLDERS AND 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.
#
#
# $Id: //info.ravenbrook.com/project/p4dti/version/1.1/test/test_message.py#3 $
