#!/usr/bin/python
"""
$Id: testcase.py,v 1.23 2006/11/24 11:51:20 dom Exp $

License
-------
Copyright (c) 2006 World Wide Web Consortium, (Massachusetts
Institute of Technology, European Research Consortium for Informatics
and Mathematics, Keio University). All Rights Reserved. This work is
distributed under the W3C Software License [1] in the hope that it
will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[1] http://www.w3.org/Consortium/Legal/copyright-software

"""

import UserList
from utils import SaxParser, LinksParser, MaxLinksNumber, HttpError
from httplib2 import RedirectLimit
import socket

import gettext
gettext.bindtextdomain('messages', 'locale')
gettext.textdomain('messages')
_ = gettext.gettext


class TestCase:
    # machine name of the best practice from which the test was derived
    BpId = []

    """ A Test Case with a run method """
    def run(self):
        pass

class URIBasedTestCase(TestCase):
    """ A Test Case whose input is taken from dereferencing a given URI """
    def addObservation(self,observation):
        observation.setSource(self.BpId)
        self._observations.append(observation)

    def _http_request(self,uri,method="GET",req_head=None):
        try:
            from utils import HTTPRequest
            http_engine = HTTPRequest()
            h,c = http_engine.http_request(uri,method,req_head)
            # there was a redirect, we need to update the URI
            # @@@ doesn't work
            #if h._previous and h._previous.has_key("location"):
            #    self.uri = h._previous["location"]
            return h,c
        except socket.error, msg:
            self.addObservations(Observation("HT1",Location(self.uri),{"method":method,"uri":uri,"msg":msg}))
            return None,None
        except RedirectLimit:
            self.addObservation(Observation("HT2",Location(self.uri),{"uri":uri}))
            return None,None
        except HttpError, headers:
            self.addObservations(Observation("HT3",Location(self.uri),{"uri":uri,"method":method,"code":headers[0]["status"]}))


    def run(self,uri,profile=None):
        self.uri=uri
        self._observations = TestResults()
        self._profile = profile
        pass

class XMLBasedTestCase(URIBasedTestCase,SaxParser):
    """ A Test Case whose input is taken from parsing an XML document
    at a given URI.
    This class implements the SAX ContentHandler interface, so one just needs to define e.g. startElement to add observations
    """
    def setup(self,uri):
        self.uri=uri
        self._headers,self._content = self._http_request(uri,"GET")
        self._observations = TestResults()

    def getObservations(self):
        return self._observations


    def run(self,uri):
        self.setup(uri)
        self.parse(uri)
        return self.getObservations()

class LinksBasedTestCase(LinksParser,XMLBasedTestCase):
    """ A Test Case that analyses the links in the given XML document"""
    def endDocument(self):
        try:
            LinksParser.endDocument(self)
        except MaxLinksNumber,msg:
            self.addObservation(Observation("EX2",Location(self.uri),{"msg":msg,"bp":self.BpId}))
        except HttpError, headers:            
            self._observations.append(Observation("HT3",Location(self.uri),{"code":headers[0]["status"],"method":"GET","uri":headers[0]["Location"]}))
        self._observeLinks()
        
    def _observeLinks(self):
        """ A method to be overridden in derivative classes"""
        pass
        
class TestResults(UserList.UserList):
    """ A TestResults is an ordered list of observations"""
    def __init__(self,observations=None):
        UserList.UserList.__init__(self,observations)

    def __eq__(self,testresults):
        if len(self)==len(testresults):
            for i in range(len(self)):
                if self[i]!=testresults[i]:
                    return 0
            return 1
        return 0

    def __ne__(self, location):
        return not self.__eq__(location)

    def getObservationById(self,id):
        for i in self.data:
            if i.id == id:
                return i

        
                
    def __contains__(self,id):
        for i in self.data:
            if i.id == id:
                return True
        return False

class Observation:
    """ An observation is a well-defined message about a specific location in the observed item; see http://esw.w3.org/topic/MarkupValidator/M12N"""
    def __init__(self,id,location,additionalInfo={}):
        self.id = id
        self.location = location
        self.additionalInfo = additionalInfo
        # the bp that triggered the observation
        self.source = None

    def setSource(self,source):
        self.source=source

    def __eq__(self,observation):
        return self.id==observation.id and self.location==observation.location and self.additionalInfo == observation.additionalInfo

    def __ne__(self, location):
        return not self.__eq__(location)

    def __repr__(self):
        if len(self.additionalInfo):
            return "Observation('%s',%s,%s)" %(self.id,repr(self.location),self.additionalInfo)
        else:
            return "Observation('%s',%s)" %(self.id,repr(self.location))


class Location:
    """ A location describes where something was observed"""
    def __init__(self,uri):
        self.uri = uri

    def __ne__(self, location):
        return not self.__eq__(location)

    def __eq__(self,location):
        return self.uri == location.uri

    def __cmp__(self,location):
        if location.__class__ == self.__class__:
            return cmp(self.uri,location.uri)
        return -cmp(location,self)

    def __repr__(self):
        return "Location('%s')" % (self.uri)

    def __str__(self):
        return _("%s") %(self.uri)

class BodyLocation(Location):
    " A location situated in the body of the observed item"
    pass

class LineColumnLocation(BodyLocation):
    " A location defined as line and a column number in the body of the observed item"
    def __init__(self,uri,encoding,line,column):
        BodyLocation.__init__(self,uri)
        self.encoding = encoding
        self.line = line
        self.column = column

    def __eq__(self,location):
        return self.uri == location.uri and self.encoding==location.encoding and self.line==location.line and self.column==location.column

    def __cmp__(self,other):
        if hasattr(other,"line") and hasattr(other,"column"):
            return cmp(2*cmp(self.line,other.line) + cmp(self.column,other.column),0)
        else:
            return cmp(1,0)

    def __repr__(self):
        return "LineColumnLocation('%s','%s',%d,%d)" % (self.uri,self.encoding,self.line,self.column)

    def __str__(self):
        return _("line %d column %d ") % (self.line,self.column)


class HeaderLocation(Location):
    " A loaction defined in the headers of the observed item"
    def __init__(self,uri,header):
        Location.__init__(self,uri)
        self.header = header

    def __eq__(self,location):
        return self.uri == location.uri and self.header == location.header

    def __repr__(self):
        return "HeaderLocation('%s','%s')" % (self.uri,self.header)

    def __str__(self):
        return _("HTTP header %s") % (self.header)

import unittest

class Tests(unittest.TestCase):
    def testLocationsOrdering(self):
        # a LineColumnLocation should be compared greater than a Location
        # so that error on the page as a whole appear before error in the body
        l = Location("http://example.org/")
        m = LineColumnLocation("http://example.org/","utf-8",2,3)
        self.failUnless(l < m)


def _test():
    unittest.main()

if __name__ == '__main__':
    _test()


