incubator-heraldry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ket...@apache.org
Subject svn commit: r463036 [3/4] - in /incubator/heraldry/libraries/python/yadis/trunk: ./ admin/ doc/ examples/ examples/apache/ yadis/ yadis/test/ yadis/test/data/
Date Wed, 11 Oct 2006 23:09:14 GMT
Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/filters.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/filters.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/filters.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/filters.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,200 @@
+"""This module contains functions and classes used for extracting
+endpoint information out of a Yadis XRD file using the ElementTree XML
+parser.
+"""
+
+__all__ = [
+    'BasicServiceEndpoint',
+    'mkFilter',
+    'IFilter',
+    'TransformFilterMaker',
+    'CompoundFilter',
+    ]
+
+from yadis.etxrd import expandService
+
+class BasicServiceEndpoint(object):
+    """Generic endpoint object that contains parsed service
+    information, as well as a reference to the service element from
+    which it was generated. If there is more than one xrd:Type or
+    xrd:URI in the xrd:Service, this object represents just one of
+    those pairs.
+
+    This object can be used as a filter, because it implements
+    fromBasicServiceEndpoint.
+
+    The simplest kind of filter you can write implements
+    fromBasicServiceEndpoint, which takes one of these objects.
+    """
+    def __init__(self, yadis_url, type_uris, uri, service_element):
+        self.type_uris = type_uris
+        self.yadis_url = yadis_url
+        self.uri = uri
+        self.service_element = service_element
+
+    def matchTypes(self, type_uris):
+        """Query this endpoint to see if it has any of the given type
+        URIs. This is useful for implementing other endpoint classes
+        that e.g. need to check for the presence of multiple versions
+        of a single protocol.
+
+        @param type_uris: The URIs that you wish to check
+        @type type_uris: iterable of str
+
+        @return: all types that are in both in type_uris and
+            self.type_uris
+        """
+        return [uri for uri in type_uris if uri in self.type_uris]
+
+    def fromBasicServiceEndpoint(endpoint):
+        """Trivial transform from a basic endpoint to itself. This
+        method exists to allow BasicServiceEndpoint to be used as a
+        filter.
+
+        If you are subclassing this object, re-implement this function.
+
+        @param endpoint: An instance of BasicServiceEndpoint
+        @return: The object that was passed in, with no processing.
+        """
+        return endpoint
+
+    fromBasicServiceEndpoint = staticmethod(fromBasicServiceEndpoint)
+
+class IFilter(object):
+    """Interface for Yadis filter objects. Other filter-like things
+    are convertable to this class."""
+
+    def getServiceEndpoints(self, yadis_url, service_element):
+        """Returns an iterator of endpoint objects"""
+        raise NotImplementedError
+
+class TransformFilterMaker(object):
+    """Take a list of basic filters and makes a filter that transforms
+    the basic filter into a top-level filter. This is mostly useful
+    for the implementation of mkFilter, which should only be needed
+    for special cases or internal use by this library.
+
+    This object is useful for creating simple filters for services
+    that use one URI and are specified by one Type (we expect most
+    Types will fit this paradigm).
+
+    Creates a BasicServiceEndpoint object and apply the filter
+    functions to it until one of them returns a value.
+    """
+
+    def __init__(self, filter_functions):
+        """Initialize the filter maker's state
+
+        @param filter_functions: The endpoint transformer functions to
+            apply to the basic endpoint. These are called in turn
+            until one of them does not return None, and the result of
+            that transformer is returned.
+        """
+        self.filter_functions = filter_functions
+
+    def getServiceEndpoints(self, yadis_url, service_element):
+        """Returns an iterator of endpoint objects produced by the
+        filter functions."""
+        endpoints = []
+
+        # Do an expansion of the service element by xrd:Type and xrd:URI
+        for type_uris, uri, _ in expandService(service_element):
+
+            # Create a basic endpoint object to represent this
+            # yadis_url, Service, Type, URI combination
+            endpoint = BasicServiceEndpoint(
+                yadis_url, type_uris, uri, service_element)
+
+            e = self.applyFilters(endpoint)
+            if e is not None:
+                endpoints.append(e)
+
+        return endpoints
+
+    def applyFilters(self, endpoint):
+        """Apply filter functions to an endpoint until one of them
+        returns non-None."""
+        for filter_function in self.filter_functions:
+            e = filter_function(endpoint)
+            if e is not None:
+                # Once one of the filters has returned an
+                # endpoint, do not apply any more.
+                return e
+
+        return None
+
+class CompoundFilter(object):
+    """Create a new filter that applies a set of filters to an endpoint
+    and collects their results.
+    """
+    def __init__(self, subfilters):
+        self.subfilters = subfilters
+
+    def getServiceEndpoints(self, yadis_url, service_element):
+        """Generate all endpoint objects for all of the subfilters of
+        this filter and return their concatenation."""
+        endpoints = []
+        for subfilter in self.subfilters:
+            endpoints.extend(
+                subfilter.getServiceEndpoints(yadis_url, service_element))
+        return endpoints
+
+# Exception raised when something is not able to be turned into a filter
+filter_type_error = TypeError(
+    'Expected a filter, an endpoint, a callable or a list of any of these.')
+
+def mkFilter(parts):
+    """Convert a filter-convertable thing into a filter
+
+    @param parts: a filter, an endpoint, a callable, or a list of any of these.
+    """
+    # Convert the parts into a list, and pass to mkCompoundFilter
+    if parts is None:
+        parts = [BasicServiceEndpoint]
+
+    try:
+        parts = list(parts)
+    except TypeError:
+        return mkCompoundFilter([parts])
+    else:
+        return mkCompoundFilter(parts)
+
+def mkCompoundFilter(parts):
+    """Create a filter out of a list of filter-like things
+
+    Used by mkFilter
+
+    @param parts: list of filter, endpoint, callable or list of any of these
+    """
+    # Separate into a list of callables and a list of filter objects
+    transformers = []
+    filters = []
+    for subfilter in parts:
+        try:
+            subfilter = list(subfilter)
+        except TypeError:
+            # If it's not an iterable
+            if hasattr(subfilter, 'getServiceEndpoints'):
+                # It's a full filter
+                filters.append(subfilter)
+            elif hasattr(subfilter, 'fromBasicServiceEndpoint'):
+                # It's an endpoint object, so put its endpoint
+                # conversion attribute into the list of endpoint
+                # transformers
+                transformers.append(subfilter.fromBasicServiceEndpoint)
+            elif callable(subfilter):
+                # It's a simple callable, so add it to the list of
+                # endpoint transformers
+                transformers.append(subfilter)
+            else:
+                raise filter_type_error
+        else:
+            filters.append(mkCompoundFilter(subfilter))
+
+    if transformers:
+        filters.append(TransformFilterMaker(transformers))
+
+    if len(filters) == 1:
+        return filters[0]
+    else:
+        return CompoundFilter(filters)

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/manager.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/manager.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/manager.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/manager.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,185 @@
+class YadisServiceManager(object):
+    """Holds the state of a list of selected Yadis services, managing
+    storing it in a session and iterating over the services in order."""
+
+    def __init__(self, starting_url, yadis_url, services, session_key):
+        # The URL that was used to initiate the Yadis protocol
+        self.starting_url = starting_url
+
+        # The URL after following redirects (the identifier)
+        self.yadis_url = yadis_url
+
+        # List of service elements
+        self.services = list(services)
+
+        self.session_key = session_key
+
+        # Reference to the current service object
+        self._current = None
+
+    def __len__(self):
+        """How many untried services remain?"""
+        return len(self.services)
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        """Return the next service
+
+        self.current() will continue to return that service until the
+        next call to this method."""
+        try:
+            self._current = self.services.pop(0)
+        except IndexError:
+            raise StopIteration
+        else:
+            return self._current
+
+    def current(self):
+        """Return the current service.
+
+        Returns None if there are no services left.
+        """
+        return self._current
+
+    def forURL(self, url):
+        return url in [self.starting_url, self.yadis_url]
+
+    def started(self):
+        """Has the first service been returned?"""
+        return self._current is not None
+
+    def store(self, session):
+        """Store this object in the session, by its session key."""
+        session[self.session_key] = self
+
+class Discovery(object):
+    """State management for discovery.
+
+    High-level usage pattern is to call .getNextService(discover) in
+    order to find the next available service for this user for this
+    session. Once a request completes, call .finish() to clean up the
+    session state.
+
+    @ivar session: a dict-like object that stores state unique to the
+        requesting user-agent. This object must be able to store
+        serializable objects.
+
+    @ivar url: the URL that is used to make the discovery request
+
+    @ivar session_key_suffix: The suffix that will be used to identify
+        this object in the session object.
+    """
+
+    DEFAULT_SUFFIX = 'auth'
+    PREFIX = '_yadis_services_'
+
+    def __init__(self, session, url, session_key_suffix=None):
+        """Initialize a discovery object"""
+        self.session = session
+        self.url = url
+        if session_key_suffix is None:
+            session_key_suffix = self.DEFAULT_SUFFIX
+
+        self.session_key_suffix = session_key_suffix
+
+    def getNextService(self, discover):
+        """Return the next authentication service for the pair of
+        user_input and session.  This function handles fallback.
+
+
+        @param discover: a callable that takes a URL and returns a
+            list of services
+
+        @type discover: str -> [service]
+
+
+        @return: the next available service
+        """
+        manager = self.getManager()
+        if manager is not None and not manager:
+            self.destroyManager()
+
+        if not manager:
+            yadis_url, services = discover(self.url)
+            manager = self.createManager(services, yadis_url)
+
+        if manager:
+            service = manager.next()
+            manager.store(self.session)
+        else:
+            service = None
+
+        return service
+
+    def cleanup(self):
+        """Clean up Yadis-related services in the session and return
+        the most-recently-attempted service from the manager, if one
+        exists.
+
+        @return: current service endpoint object or None if there is
+            no current service
+        """
+        manager = self.getManager()
+        if manager is not None:
+            service = manager.current()
+            self.destroyManager()
+        else:
+            service = None
+
+        return service
+
+    ### Lower-level methods
+
+    def getSessionKey(self):
+        """Get the session key for this starting URL and suffix
+
+        @return: The session key
+        @rtype: str
+        """
+        return self.PREFIX + self.session_key_suffix
+
+    def getManager(self):
+        """Extract the YadisServiceManager for this object's URL and
+        suffix from the session.
+
+        @return: The current YadisServiceManager, if it's for this
+            URL, or else None
+        """
+        manager = self.session.get(self.getSessionKey())
+        if (manager is not None and manager.forURL(self.url)):
+            return manager
+        else:
+            return None
+
+    def createManager(self, services, yadis_url=None):
+        """Create a new YadisService Manager for this starting URL and
+        suffix, and store it in the session.
+
+        @raises KeyError: When I already have a manager.
+
+        @return: A new YadisServiceManager or None
+        """
+        key = self.getSessionKey()
+        if self.getManager():
+            raise KeyError('There is already a %r manager for %r' %
+                           (key, self.url))
+
+        if not services:
+            return None
+
+        manager = YadisServiceManager(self.url, yadis_url, services, key)
+        manager.store(self.session)
+        return manager
+
+    def destroyManager(self):
+        """Delete any YadisServiceManager with this starting URL and
+        suffix from the session.
+
+        If there is no service manager or the service manager is for a
+        different URL, it silently does nothing.
+        """
+        if self.getManager() is not None:
+            key = self.getSessionKey()
+            del self.session[key]

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/parsehtml.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/parsehtml.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/parsehtml.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/parsehtml.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,197 @@
+__all__ = ['findHTMLMeta', 'MetaNotFound']
+
+from HTMLParser import HTMLParser, HTMLParseError
+import htmlentitydefs
+import re
+
+from yadis.constants import YADIS_HEADER_NAME
+
+# Size of the chunks to search at a time (also the amount that gets
+# read at a time)
+CHUNK_SIZE = 1024 * 16 # 16 KB
+
+class ParseDone(Exception):
+    """Exception to hold the URI that was located when the parse is
+    finished. If the parse finishes without finding the URI, set it to
+    None."""
+
+class MetaNotFound(Exception):
+    """Exception to hold the content of the page if we did not find
+    the appropriate <meta> tag"""
+
+re_flags = re.IGNORECASE | re.UNICODE | re.VERBOSE
+ent_pat = r'''
+&
+
+(?: \#x (?P<hex> [a-f0-9]+ )
+|   \# (?P<dec> \d+ )
+|   (?P<word> \w+ )
+)
+
+;'''
+
+ent_re = re.compile(ent_pat, re_flags)
+
+def substituteMO(mo):
+    if mo.lastgroup == 'hex':
+        codepoint = int(mo.group('hex'), 16)
+    elif mo.lastgroup == 'dec':
+        codepoint = int(mo.group('dec'))
+    else:
+        assert mo.lastgroup == 'word'
+        codepoint = htmlentitydefs.name2codepoint.get(mo.group('word'))
+
+    if codepoint is None:
+        return mo.group()
+    else:
+        return unichr(codepoint)
+
+def substituteEntities(s):
+    return ent_re.sub(substituteMO, s)
+
+class YadisHTMLParser(HTMLParser):
+    """Parser that finds a meta http-equiv tag in the head of a html
+    document.
+
+    When feeding in data, if the tag is matched or it will never be
+    found, the parser will raise ParseDone with the uri as the first
+    attribute.
+
+    Parsing state diagram
+    =====================
+
+    Any unlisted input does not affect the state::
+
+                1, 2, 5                       8
+               +--------------------------+  +-+
+               |                          |  | |
+            4  |    3       1, 2, 5, 7    v  | v
+        TOP -> HTML -> HEAD ----------> TERMINATED
+        | |            ^  |               ^  ^
+        | | 3          |  |               |  |
+        | +------------+  +-> FOUND ------+  |
+        |                  6         8       |
+        | 1, 2                               |
+        +------------------------------------+
+
+      1. any of </body>, </html>, </head> -> TERMINATE
+      2. <body> -> TERMINATE
+      3. <head> -> HEAD
+      4. <html> -> HTML
+      5. <html> -> TERMINATE
+      6. <meta http-equiv='X-XRDS-Location'> -> FOUND
+      7. <head> -> TERMINATE
+      8. Any input -> TERMINATE
+    """
+    TOP = 0
+    HTML = 1
+    HEAD = 2
+    FOUND = 3
+    TERMINATED = 4
+
+    def __init__(self):
+        HTMLParser.__init__(self)
+        self.phase = self.TOP
+
+    def _terminate(self):
+        self.phase = self.TERMINATED
+        raise ParseDone(None)
+
+    def handle_endtag(self, tag):
+        # If we ever see an end of head, body, or html, bail out right away.
+        # [1]
+        if tag in ['head', 'body', 'html']:
+            self._terminate()
+
+    def handle_starttag(self, tag, attrs):
+        # if we ever see a start body tag, bail out right away, since
+        # we want to prevent the meta tag from appearing in the body
+        # [2]
+        if tag=='body':
+            self._terminate()
+
+        if self.phase == self.TOP:
+            # At the top level, allow a html tag or a head tag to move
+            # to the head or html phase
+            if tag == 'head':
+                # [3]
+                self.phase = self.HEAD
+            elif tag == 'html':
+                # [4]
+                self.phase = self.HTML
+
+        elif self.phase == self.HTML:
+            # if we are in the html tag, allow a head tag to move to
+            # the HEAD phase. If we get another html tag, then bail
+            # out
+            if tag == 'head':
+                # [3]
+                self.phase = self.HEAD
+            elif tag == 'html':
+                # [5]
+                self._terminate()
+
+        elif self.phase == self.HEAD:
+            # If we are in the head phase, look for the appropriate
+            # meta tag. If we get a head or body tag, bail out.
+            if tag == 'meta':
+                attrs_d = dict(attrs)
+                http_equiv = attrs_d.get('http-equiv', '').lower()
+                if http_equiv == YADIS_HEADER_NAME.lower():
+                    raw_attr = attrs_d.get('content')
+                    yadis_loc = substituteEntities(raw_attr)
+                    # [6]
+                    self.phase = self.FOUND
+                    raise ParseDone(yadis_loc)
+
+            elif tag in ['head', 'html']:
+                # [5], [7]
+                self._terminate()
+
+    def feed(self, chars):
+        # [8]
+        if self.phase in [self.TERMINATED, self.FOUND]:
+            self._terminate()
+
+        return HTMLParser.feed(self, chars)
+
+def findHTMLMeta(stream):
+    """Look for a meta http-equiv tag with the YADIS header name.
+
+    @param stream: Source of the html text
+    @type stream: Object that implements a read() method that works
+        like file.read
+
+    @return: The URI from which to fetch the XRDS document
+    @rtype: str
+
+    @raises MetaNotFound: raised with the content that was
+        searched as the first parameter.
+    """
+    parser = YadisHTMLParser()
+    chunks = []
+
+    while 1:
+        chunk = stream.read(CHUNK_SIZE)
+        if not chunk:
+            # End of file
+            break
+
+        chunks.append(chunk)
+        try:
+            parser.feed(chunk)
+        except HTMLParseError, why:
+            # HTML parse error, so bail
+            chunks.append(stream.read())
+            break
+        except ParseDone, why:
+            uri = why[0]
+            if uri is None:
+                # Parse finished, but we may need the rest of the file
+                chunks.append(stream.read())
+                break
+            else:
+                return uri
+
+    content = ''.join(chunks)
+    raise MetaNotFound(content)

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/services.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/services.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/services.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/services.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,46 @@
+from yadis.filters import mkFilter
+from yadis.discover import discover
+from yadis.etxrd import parseXRDS, iterServices
+
+def getServiceEndpoints(input_url, flt=None):
+    """Perform the Yadis protocol on the input URL and return an
+    iterable of resulting endpoint objects.
+
+    @param flt: A filter object or something that is convertable to
+        a filter object (using mkFilter) that will be used to generate
+        endpoint objects. This defaults to generating BasicEndpoint
+        objects.
+
+    @param input_url: The URL on which to perform the Yadis protocol
+
+    @return: The normalized identity URL and an iterable of endpoint
+        objects generated by the filter function.
+
+    @rtype: (str, [endpoint])
+    """
+    result = discover(input_url)
+    endpoints = applyFilter(result.normalized_uri, result.response_text, flt)
+    return (result.normalized_uri, endpoints)
+
+def applyFilter(normalized_uri, xrd_data, flt=None):
+    """Generate an iterable of endpoint objects given this input data,
+    presumably from the result of performing the Yadis protocol.
+
+    @param normalized_uri: The input URL, after following redirects,
+        as in the Yadis protocol.
+
+
+    @param xrd_data: The XML text the XRDS file fetched from the
+        normalized URI.
+    @type xrd_data: str
+
+    """
+    flt = mkFilter(flt)
+    et = parseXRDS(xrd_data)
+
+    endpoints = []
+    for service_element in iterServices(et):
+        endpoints.extend(
+            flt.getServiceEndpoints(normalized_uri, service_element))
+
+    return endpoints

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/__init__.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/__init__.py?view=auto&rev=463036
==============================================================================
    (empty)

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/README
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/README?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/README (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/README Wed Oct 11 16:09:11 2006
@@ -0,0 +1,12 @@
+delegated-20060809.xrds    - results from proxy.xri.net, determined by 
+                             Drummond and Kevin to be incorrect.
+delegated-20060809-r1.xrds - Drummond's 1st correction
+delegated-20060809-r2.xrds - Drummond's 2nd correction
+
+spoofs: keturn's (=!E4)'s attempts to log in with Drummond's i-number (=!D2)
+spoof1.xrds
+spoof2.xrds
+spoof3.xrds - attempt to steal @!C0!D2 by having "at least one" CanonicalID
+    match the $res service ProviderID.
+
+ref.xrds - resolving @ootao*test.ref, which refers to a neustar XRI.

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/accept.txt
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/accept.txt?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/accept.txt (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/accept.txt Wed Oct 11 16:09:11 2006
@@ -0,0 +1,118 @@
+# Accept: [Accept: header value from RFC2616,
+#     http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html]
+# Available: [whitespace-separated content types]
+# Expected: [Accept-header like list, containing the available content
+#     types with their q-values]
+
+Accept: */*
+Available: text/plain
+Expected: text/plain; q=1.0
+
+Accept: */*
+Available: text/plain, text/html
+Expected: text/plain; q=1.0, text/html; q=1.0
+
+# The order matters
+Accept: */*
+Available: text/html, text/plain
+Expected: text/html; q=1.0, text/plain; q=1.0
+
+Accept: text/*, */*; q=0.9
+Available: text/plain, image/jpeg
+Expected: text/plain; q=1.0, image/jpeg; q=0.9
+
+Accept: text/*, */*; q=0.9
+Available: image/jpeg, text/plain
+Expected: text/plain; q=1.0, image/jpeg; q=0.9
+
+# wildcard subtypes still reject differing main types
+Accept: text/*
+Available: image/jpeg, text/plain
+Expected: text/plain; q=1.0
+
+Accept: text/html
+Available: text/html
+Expected: text/html; q=1.0
+
+Accept: text/html, text/*
+Available: text/html
+Expected: text/html; q=1.0
+
+Accept: text/html, text/*
+Available: text/plain, text/html
+Expected: text/plain; q=1.0, text/html; q=1.0
+
+Accept: text/html, text/*; q=0.9
+Available: text/plain, text/html
+Expected: text/html; q=1.0, text/plain; q=0.9
+
+# If a more specific type has a higher q-value, then the higher value wins
+Accept: text/*; q=0.9, text/html
+Available: text/plain, text/html
+Expected: text/html; q=1.0, text/plain; q=0.9
+
+Accept: */*, text/*; q=0.9, text/html; q=0.1
+Available: text/plain, text/html, image/monkeys
+Expected: image/monkeys; q=1.0, text/plain; q=0.9, text/html; q=0.1
+
+Accept: text/*, text/html; q=0
+Available: text/html
+Expected:
+
+Accept: text/*, text/html; q=0
+Available: text/html, text/plain
+Expected: text/plain; q=1.0
+
+Accept: text/html
+Available: text/plain
+Expected:
+
+Accept: application/xrds+xml, text/html; q=0.9
+Available: application/xrds+xml, text/html
+Expected: application/xrds+xml; q=1.0, text/html; q=0.9
+
+Accept: application/xrds+xml, */*; q=0.9
+Available: application/xrds+xml, text/html
+Expected: application/xrds+xml; q=1.0, text/html; q=0.9
+
+Accept: application/xrds+xml, application/xhtml+xml; q=0.9, text/html; q=0.8, text/xml; q=0.7
+Available: application/xrds+xml, text/html
+Expected: application/xrds+xml; q=1.0, text/html; q=0.8
+
+# See http://www.rfc-editor.org/rfc/rfc3023.txt, section A.13
+Accept: application/xrds
+Available: application/xrds+xml
+Expected:
+
+Accept: application/xrds+xml
+Available: application/xrds
+Expected:
+
+Accept: application/xml
+Available: application/xrds+xml
+Expected:
+
+Available: application/xrds+xml
+Accept: application/xml
+Expected:
+
+
+
+#################################################
+# The tests below this line are documentation of how this library
+# works. If the implementation changes, it's acceptable to change the
+# test to reflect that. These are specified so that we can make sure
+# that the current implementation actually works the way that we
+# expect it to given these inputs.
+
+Accept: text/html;level=1
+Available: text/html
+Expected: text/html; q=1.0
+
+Accept: text/html; level=1, text/html; level=9; q=0.1
+Available: text/html
+Expected: text/html; q=1.0
+
+Accept: text/html; level=9; q=0.1, text/html; level=1
+Available: text/html
+Expected: text/html; q=1.0

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r1.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r1.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r1.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r1.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*ootao</Query>
+  <Status code="100"/>
+  <Expires>2006-08-09T22:07:13.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
+  <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
+  <Service priority="10">
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <ProviderID>xri://!!1003</ProviderID>
+   <MediaType>application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*test1</Query>
+  <Status code="100">SUCCESS</Status>
+  <ProviderID>xri://!!1003</ProviderID>
+  <LocalID>!0000.0000.3B9A.CA01</LocalID>
+  <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
+  <Service>
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r2.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r2.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r2.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809-r2.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*ootao</Query>
+  <Status code="100"/>
+  <Expires>2006-08-09T22:07:13.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
+  <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
+  <Service priority="10">
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
+   <MediaType>application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*test1</Query>
+  <Status code="100">SUCCESS</Status>
+  <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
+  <LocalID>!0000.0000.3B9A.CA01</LocalID>
+  <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
+  <Service>
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/delegated-20060809.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*ootao</Query>
+  <Status code="100"/>
+  <Expires>2006-08-09T22:07:13.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
+  <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
+  <Service priority="10">
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <ProviderID/>
+   <MediaType>application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*test1</Query>
+  <Status code="100">SUCCESS</Status>
+  <ProviderID>xri://!!1003</ProviderID>
+  <LocalID>!0000.0000.3B9A.CA01</LocalID>
+  <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
+  <Service>
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/example-xrds.xml
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/example-xrds.xml?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/example-xrds.xml (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/example-xrds.xml Wed Oct 11 16:09:11 2006
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Sample XRDS file at: NAME -->
+<xrds:XRDS
+    xmlns:xrds="xri://$xrds"
+    xmlns="xri://$xrd*($v*2.0)">
+  <XRD>
+
+    <Service priority="0">
+      <Type>http://example.com/</Type>
+      <URI>http://www.openidenabled.com/</URI>
+    </Service>
+
+  </XRD>
+</xrds:XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/no-xrd.xml
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/no-xrd.xml?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/no-xrd.xml (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/no-xrd.xml Wed Oct 11 16:09:11 2006
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS
+    xmlns:xrds="xri://$xrds"
+    xmlns:openid="http://openid.net/xmlns/1.0"
+    xmlns:typekey="http://typekey.com/xmlns/1.0"
+    xmlns="xri://$xrd*($v*2.0)">
+</xrds:XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/not-xrds.xml
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/not-xrds.xml?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/not-xrds.xml (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/not-xrds.xml Wed Oct 11 16:09:11 2006
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<x></x>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/prefixsometimes.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/prefixsometimes.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/prefixsometimes.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/prefixsometimes.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*ootao</Query>
+  <Status code="100"/>
+  <Expires>2006-08-09T22:07:13.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
+  <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
+  <Service priority="10">
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
+   <MediaType>application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*test1</Query>
+  <Status code="100">SUCCESS</Status>
+  <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
+  <LocalID>!0000.0000.3B9A.CA01</LocalID>
+  <CanonicalID>xri://@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
+  <Service>
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/ref.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/ref.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/ref.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/ref.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://@ootao*test.ref" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*ootao</Query>
+  <Status code="100"/>
+  <Expires>2006-08-15T18:56:09.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
+  <CanonicalID priority="10">@!5BAD.2AA.3C72.AF46</CanonicalID>
+  <Service priority="10">
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <ProviderID/>
+   <MediaType>application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*test.ref</Query>
+  <Status code="100">SUCCESS</Status>
+  <ProviderID>xri://!!1003</ProviderID>
+  <LocalID>!0000.0000.3B9A.CA03</LocalID>
+  <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA03</CanonicalID>
+  <Ref>@!BAE.A650.823B.2475</Ref>
+  <Service>
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRDS ref="xri://@!BAE.A650.823B.2475" xmlns="xri://$xrds">
+  <XRD xmlns="xri://$xrd*($v*2.0)">
+   <Query>!BAE.A650.823B.2475</Query>
+   <Status code="100"/>
+   <Expires>2006-08-15T18:56:10.000Z</Expires>
+   <ProviderID>xri://@</ProviderID>
+   <LocalID priority="10">!BAE.A650.823B.2475</LocalID>
+   <CanonicalID priority="10">@!BAE.A650.823B.2475</CanonicalID>
+   <Service priority="10">
+    <Type select="true">(+wdnc)</Type>
+    <ProviderID/>
+    <Path select="true">(+wdnc)</Path>
+    <URI append="none" priority="10">http://www.tcpacompliance.us</URI>
+   </Service>
+   <Service priority="10">
+    <Type match="content" select="true">xri://$res*auth*($v*2.0)</Type>
+    <ProviderID/>
+    <MediaType match="content" select="false">application/xrds+xml;trust=none</MediaType>
+    <URI priority="10">http://dev.dready.org/cgi-bin/xri</URI>
+   </Service>
+   <Service priority="10">
+    <Type match="content" select="true">(+i-name)</Type>
+    <ProviderID/>
+    <Path match="content" select="true">(+i-name)</Path>
+    <URI append="none" priority="10">http://www.inames.net</URI>
+   </Service>
+   <Service priority="10">
+    <Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type>
+    <Type match="default" select="false"/>
+    <ProviderID>xri://!!1001</ProviderID>
+    <Path select="true">(+contact)</Path>
+    <Path match="null" select="false"/>
+    <MediaType select="false">text/html</MediaType>
+    <MediaType match="default" select="false"/>
+    <URI append="none" priority="10">http://www.neustar.biz</URI>
+   </Service>
+  </XRD>
+ </XRDS>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>!BAE.A650.823B.2475</Query>
+  <Status code="100"/>
+  <Expires>2006-08-15T18:56:10.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!BAE.A650.823B.2475</LocalID>
+  <CanonicalID priority="10">@!BAE.A650.823B.2475</CanonicalID>
+  <Service priority="10">
+   <Type select="true">(+wdnc)</Type>
+   <ProviderID/>
+   <Path select="true">(+wdnc)</Path>
+   <URI append="none" priority="10">http://www.tcpacompliance.us</URI>
+  </Service>
+  <Service priority="10">
+   <Type match="content" select="true">xri://$res*auth*($v*2.0)</Type>
+   <ProviderID/>
+   <MediaType match="content" select="false">application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://dev.dready.org/cgi-bin/xri</URI>
+  </Service>
+  <Service priority="10">
+   <Type match="content" select="true">(+i-name)</Type>
+   <ProviderID/>
+   <Path match="content" select="true">(+i-name)</Path>
+   <URI append="none" priority="10">http://www.inames.net</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">xri://+i-service*(+contact)*($v*1.0)</Type>
+   <Type match="default" select="false"/>
+   <ProviderID>xri://!!1001</ProviderID>
+   <Path select="true">(+contact)</Path>
+   <Path match="null" select="false"/>
+   <MediaType select="false">text/html</MediaType>
+   <MediaType match="default" select="false"/>
+   <URI append="none" priority="10">http://www.neustar.biz</URI>
+  </Service>
+ </XRD>
+</XRDS>
\ No newline at end of file

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/sometimesprefix.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/sometimesprefix.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/sometimesprefix.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/sometimesprefix.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://@ootao*test1" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*ootao</Query>
+  <Status code="100"/>
+  <Expires>2006-08-09T22:07:13.000Z</Expires>
+  <ProviderID>xri://@</ProviderID>
+  <LocalID priority="10">!5BAD.2AA.3C72.AF46</LocalID>
+  <CanonicalID priority="10">xri://@!5BAD.2AA.3C72.AF46</CanonicalID>
+  <Service priority="10">
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
+   <MediaType>application/xrds+xml;trust=none</MediaType>
+   <URI priority="10">http://resolve.ezibroker.net/resolve/@ootao/</URI>
+  </Service>
+  <Service priority="10">
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*test1</Query>
+  <Status code="100">SUCCESS</Status>
+  <ProviderID>xri://@!5BAD.2AA.3C72.AF46</ProviderID>
+  <LocalID>!0000.0000.3B9A.CA01</LocalID>
+  <CanonicalID>@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01</CanonicalID>
+  <Service>
+   <Type select="true">http://openid.net/signon/1.0</Type>
+   <ProviderID/>
+   <URI append="qxri" priority="1">https://linksafe.ezibroker.net/server/</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof1.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof1.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof1.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof1.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+ <Query>*keturn</Query>
+ <ProviderID>xri://=</ProviderID>
+ <LocalID>!E4</LocalID>
+ <CanonicalID>=!E4</CanonicalID>
+
+ <Service>
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <URI>http://keturn.example.com/resolve/</URI>
+   <ProviderID>=!E4</ProviderID>
+ </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*isDrummond</Query>
+  <ProviderID>=!E4</ProviderID>
+  <LocalID>!D2</LocalID>
+  <CanonicalID>=!D2</CanonicalID>
+  <Service>
+    <Type>http://openid.net/signon/1.0</Type>
+    <URI>http://keturn.example.com/openid</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof2.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof2.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof2.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof2.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+ <Query>*keturn</Query>
+ <ProviderID>xri://=</ProviderID>
+ <LocalID>!E4</LocalID>
+ <CanonicalID>=!E4</CanonicalID>
+
+ <Service>
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <URI>http://keturn.example.com/resolve/</URI>
+   <ProviderID>xri://=</ProviderID>
+ </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*isDrummond</Query>
+  <ProviderID>xri://=</ProviderID>
+  <LocalID>!D2</LocalID>
+  <CanonicalID>=!D2</CanonicalID>
+  <Service>
+    <Type>http://openid.net/signon/1.0</Type>
+    <URI>http://keturn.example.com/openid</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof3.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof3.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof3.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/spoof3.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://=keturn*isDrummond" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+ <Query>*keturn</Query>
+ <ProviderID>xri://@</ProviderID>
+ <LocalID>@E4</LocalID>
+ <CanonicalID>@!E4</CanonicalID>
+
+ <Service>
+   <Type>xri://$res*auth*($v*2.0)</Type>
+   <URI>http://keturn.example.com/resolve/</URI>
+   <ProviderID>@!E4</ProviderID>
+ </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*is</Query>
+  <ProviderID>@!E4</ProviderID>
+  <LocalID>!D2</LocalID>
+  <CanonicalID>=!C0</CanonicalID>
+  <CanonicalID>=!E4!01</CanonicalID>
+  <Service>
+    <Type>xri://$res*auth*($v*2.0)</Type>
+    <URI>http://keturn.example.com/resolve/</URI>
+    <ProviderID>@!C0</ProviderID>
+  </Service>
+ </XRD>
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*drummond</Query>
+  <ProviderID>@!C0</ProviderID>
+  <LocalID>!D2</LocalID>
+  <CanonicalID>@!C0!D2</CanonicalID>
+  <Service>
+    <Type>http://openid.net/signon/1.0</Type>
+    <URI>http://keturn.example.com/openid</URI>
+  </Service>
+ </XRD>
+</XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/status222.xrds
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/status222.xrds?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/status222.xrds (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/status222.xrds Wed Oct 11 16:09:11 2006
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<XRDS ref="xri://=x" xmlns="xri://$xrds">
+ <XRD xmlns="xri://$xrd*($v*2.0)">
+  <Query>*x</Query>
+  <Status code="222">The subsegment does not exist</Status>
+  <Expires>2006-08-18T00:02:35.000Z</Expires>
+  <ProviderID>xri://=</ProviderID>
+ </XRD>
+</XRDS>
\ No newline at end of file

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-discover.txt
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-discover.txt?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-discover.txt (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-discover.txt Wed Oct 11 16:09:11 2006
@@ -0,0 +1,137 @@
+equiv
+Status: 200 OK
+Content-Type: text/html
+
+<html>
+<head>
+<meta http-equiv="YADIS_HEADER" content="URL_BASE/xrds">
+<title>Joe Schmoe's Homepage</title>
+</head>
+<body>
+<h1>Joe Schmoe's Homepage</h1>
+<p>Blah blah blah blah blah blah blah</p>
+</body>
+</html>
+
+header
+Status: 200 OK
+Content-Type: text/html
+YADIS_HEADER: URL_BASE/xrds
+
+<html>
+<head>
+<title>Joe Schmoe's Homepage</title>
+</head>
+<body>
+<h1>Joe Schmoe's Homepage</h1>
+<p>Blah blah blah blah blah blah blah</p>
+</body>
+
+xrds
+Status: 200 OK
+Content-Type: application/xrds+xml
+
+<XRDS Content>
+
+xrds_ctparam
+Status: 200 OK
+Content-Type: application/xrds+xml; charset=UTF8
+
+<XRDS Content>
+
+xrds_ctcase
+Status: 200 OK
+Content-Type: appliCATION/XRDS+xml
+
+<XRDS Content>
+
+xrds_html
+Status: 200 OK
+Content-Type: text/html
+
+<XRDS Content>
+
+redir_equiv
+Status: 302 Found
+Content-Type: text/plain
+Location: URL_BASE/equiv
+
+You are presently being redirected.
+
+redir_header
+Status: 302 Found
+Content-Type: text/plain
+Location: URL_BASE/header
+
+You are presently being redirected.
+
+redir_xrds
+Status: 302 Found
+Content-Type: application/xrds+xml
+Location: URL_BASE/xrds
+
+<XRDS Content>
+
+redir_xrds_html
+Status: 302 Found
+Content-Type: text/plain
+Location: URL_BASE/xrds_html
+
+You are presently being redirected.
+
+redir_redir_equiv
+Status: 302 Found
+Content-Type: text/plain
+Location: URL_BASE/redir_equiv
+
+You are presently being redirected.
+
+lowercase_header
+Status: 200 OK
+Content-Type: text/html
+x-xrds-location: URL_BASE/xrds
+
+<html>
+<head>
+<title>Joe Schmoe's Homepage</title>
+</head>
+<body>
+<h1>Joe Schmoe's Homepage</h1>
+<p>Blah blah blah blah blah blah blah</p>
+</body>
+
+404_server_response
+Status: 404 Not Found
+
+EEk!
+
+500_server_response
+Status: 500 Server error
+
+EEk!
+
+201_server_response
+Status: 201 Created
+
+EEk!
+
+404_with_header
+Status: 404 Not Found
+YADIS_HEADER: URL_BASE/xrds
+
+EEk!
+
+404_with_meta
+Status: 404 Not Found
+Content-Type: text/html
+
+<html>
+<head>
+<meta http-equiv="YADIS_HEADER" content="URL_BASE/xrds">
+<title>Joe Schmoe's Homepage</title>
+</head>
+<body>
+<h1>Joe Schmoe's Homepage</h1>
+<p>Blah blah blah blah blah blah blah</p>
+</body>
+</html>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-parsehtml.txt
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-parsehtml.txt?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-parsehtml.txt (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-parsehtml.txt Wed Oct 11 16:09:11 2006
@@ -0,0 +1,128 @@
+found
+<!-- minimal well-formed success case -->
+<html><head><meta http-equiv="X-XRDS-Location" content="found"></head></html>
+
+found
+<!-- minimal success case -->
+<html><head><meta http-equiv="X-XRDS-Location" content="found">
+
+found
+<!-- ignore bogus top-level tags -->
+</porky><html><head><meta http-equiv="X-XRDS-Location" content="found">
+
+found
+<!-- Case folding for header name -->
+<html><head><meta http-equiv="x-xrds-location" content="found">
+
+found
+<!-- missing <html> tag -->
+<head><meta http-equiv="X-XRDS-Location" content="found">
+
+found
+<!-- case folding for tag names -->
+<html><head><META http-equiv="X-XRDS-Location" content="found">
+
+found
+<!-- Stop after first one found -->
+<html><head>
+<meta http-equiv="x-xrds-location" content="found">
+<meta http-equiv="x-xrds-location" content="not-found">
+
+&
+<!-- standard entity -->
+<head><meta http-equiv="X-XRDS-Location" content="&amp;">
+
+found
+<!-- hex entity -->
+<html>
+  <head>
+    <meta http-equiv="X-XRDS-Location" content="&#x66;ound">
+  </head>
+</html>
+
+found
+<!-- decimal entity -->
+<html>
+  <head>
+    <meta http-equiv="X-XRDS-Location" content="&#102;ound">
+  </head>
+</html>
+
+/
+<!-- hex entity -->
+<html>
+  <head>
+    <meta http-equiv="X-XRDS-Location" content="&#x2f;">
+  </head>
+</html>
+
+
+<!-- empty string -->
+<html><head><meta http-equiv="X-XRDS-Location" content="">
+
+EOF
+<!-- No markup, except this comment -->
+
+None
+<!-- No meta, just standard HTML -->
+<html>
+  <head>
+    <title>A boring document</title>
+  </head>
+  <body>
+    <h1>A boring document</h1>
+    <p>There's really nothing interesting about this</p>
+  </body>
+</html>
+
+EOF
+<!-- No <html> or <head> -->
+<meta http-equiv="X-XRDS-Location" content="found">
+
+EOF
+<!-- No <head> tag -->
+<html><meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!-- No <html> or <head> and a <body> -->
+<body><meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!-- <head> and <html> reversed -->
+<head><html><meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!-- <meta> is inside of <body> -->
+<html><head><body><meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!-- <meta> is inside of <body> -->
+<html>
+  <head>
+    <title>Someone's blog</title>
+  </head>
+  <body>
+    <h1>My blog</h1>
+    <p>This is my blog</p>
+    <h2>Comments</h2>
+    <p><meta http-equiv="X-XRDS-Location" content="found"></p>
+  </body>
+</html>
+
+None
+<!-- short head tag -->
+<html><head/>
+<meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!-- <body> comes first -->
+<body><html><head>
+<meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!-- </body> comes first -->
+</body><html><head>
+<meta http-equiv="X-XRDS-Location" content="found">
+
+None
+<!bad processing instruction!>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-xrd.xml
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-xrd.xml?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-xrd.xml (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/data/test1-xrd.xml Wed Oct 11 16:09:11 2006
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS
+    xmlns:xrds="xri://$xrds"
+    xmlns:openid="http://openid.net/xmlns/1.0"
+    xmlns:typekey="http://typekey.com/xmlns/1.0"
+    xmlns="xri://$xrd*($v*2.0)">
+  <XRD>
+
+    <Service priority="0">
+      <Type>http://openid.net/signon/1.0</Type>
+      <URI>http://www.myopenid.com/server</URI>
+      <openid:Delegate>http://josh.myopenid.com/</openid:Delegate>
+    </Service>
+
+    <Service priority="20">
+      <Type>http://lid.netmesh.org/sso/2.0b5</Type>
+      <Type>http://lid.netmesh.org/2.0b5</Type>
+      <URI>http://mylid.net/josh</URI>
+    </Service>
+
+    <Service priority="10">
+      <Type>http://openid.net/signon/1.0</Type>
+      <URI>http://www.livejournal.com/openid/server.bml</URI>
+      <openid:Delegate>http://www.livejournal.com/users/nedthealpaca/</openid:Delegate>
+    </Service>
+
+    <Service priority="15">
+      <Type>http://typekey.com/services/1.0</Type>
+      <typekey:MemberName>joshhoyt</typekey:MemberName>
+    </Service>
+
+    <Service priority="5">
+      <Type>http://openid.net/signon/1.0</Type>
+      <URI>http://www.schtuff.com/openid</URI>
+      <openid:Delegate>http://users.schtuff.com/josh</openid:Delegate>
+    </Service>
+
+  </XRD>
+</xrds:XRDS>

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/discoverdata.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/discoverdata.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/discoverdata.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/discoverdata.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,125 @@
+"""Module to make discovery data test cases available"""
+import urlparse
+import os.path
+
+from yadis.discover import DiscoveryResult, DiscoveryFailure
+from yadis.constants import YADIS_HEADER_NAME
+
+tests_dir = os.path.dirname(__file__)
+data_path = os.path.join(tests_dir, 'data')
+
+testlist = [
+# success,  input_name,          id_name,            result_name
+    (True,  "equiv",             "equiv",            "xrds"),
+    (True,  "header",            "header",           "xrds"),
+    (True,  "lowercase_header",  "lowercase_header", "xrds"),
+    (True,  "xrds",              "xrds",             "xrds"),
+    (True,  "xrds_ctparam",      "xrds_ctparam",     "xrds_ctparam"),
+    (True,  "xrds_ctcase",       "xrds_ctcase",      "xrds_ctcase"),
+    (False, "xrds_html",         "xrds_html",        "xrds_html"),
+    (True,  "redir_equiv",       "equiv",            "xrds"),
+    (True,  "redir_header",      "header",           "xrds"),
+    (True,  "redir_xrds",        "xrds",             "xrds"),
+    (False, "redir_xrds_html",   "xrds_html",        "xrds_html"),
+    (True,  "redir_redir_equiv", "equiv",            "xrds"),
+    (False, "404_server_response", None,             None),
+    (False, "404_with_header",     None,             None),
+    (False, "404_with_meta",       None,             None),
+    (False, "201_server_response", None,             None),
+    (False, "500_server_response", None,             None),
+    ]
+
+def getDataName(*components):
+    sanitized = []
+    for part in components:
+        if part in ['.', '..']:
+            raise ValueError
+        elif part:
+            sanitized.append(part)
+
+    if not sanitized:
+        raise ValueError
+
+    return os.path.join(data_path, *sanitized)
+
+def getExampleXRDS():
+    filename = getDataName('example-xrds.xml')
+    return file(filename).read()
+
+example_xrds = getExampleXRDS()
+default_test_file = getDataName('test1-discover.txt')
+
+discover_tests = {}
+
+def readTests(filename):
+    data = file(filename).read()
+    tests = {}
+    for case in data.split('\f\n'):
+        (name, content) = case.split('\n', 1)
+        tests[name] = content
+    return tests
+
+def getData(filename, name):
+    global discover_tests
+    try:
+        file_tests = discover_tests[filename]
+    except KeyError:
+        file_tests = discover_tests[filename] = readTests(filename)
+    return file_tests[name]
+
+def fillTemplate(test_name, template, base_url, example_xrds):
+    mapping = [
+        ('URL_BASE/', base_url),
+        ('<XRDS Content>', example_xrds),
+        ('YADIS_HEADER', YADIS_HEADER_NAME),
+        ('NAME', test_name),
+        ]
+
+    for k, v in mapping:
+        template = template.replace(k, v)
+
+    return template
+
+def generateSample(test_name, base_url,
+                   example_xrds=example_xrds,
+                   filename=default_test_file):
+    try:
+        template = getData(filename, test_name)
+    except IOError, why:
+        import errno
+        if why[0] == errno.ENOENT:
+            raise KeyError(filename)
+        else:
+            raise
+
+    return fillTemplate(test_name, template, base_url, example_xrds)
+
+def generateResult(base_url, input_name, id_name, result_name, success):
+    input_url = urlparse.urljoin(base_url, input_name)
+
+    # If the name is None then we expect the protocol to fail, which
+    # we represent by None
+    if id_name is None:
+        assert result_name is None
+        return input_url, DiscoveryFailure
+
+    result = generateSample(result_name, base_url)
+    headers, content = result.split('\n\n', 1)
+    header_lines = headers.split('\n')
+    for header_line in header_lines:
+        if header_line.startswith('Content-Type:'):
+            _, ctype = header_line.split(':', 1)
+            ctype = ctype.strip()
+            break
+    else:
+        ctype = None
+
+    id_url = urlparse.urljoin(base_url, id_name)
+
+    result = DiscoveryResult(input_url)
+    result.normalized_uri = id_url
+    if success:
+        result.xrds_uri = urlparse.urljoin(base_url, result_name)
+    result.content_type = ctype
+    result.response_text = content
+    return input_url, result

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/livediscover.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/livediscover.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/livediscover.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/livediscover.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,65 @@
+import unittest
+import urllib2
+
+from yadis.discover import discover
+
+def parseManifest(lines):
+    cases = []
+    for line in lines:
+        if line.startswith('#') or not line.strip():
+            continue
+
+        # remove newline
+        assert line[-1] == '\n'
+        line = line[:-1]
+
+        # split fields
+        parts = line.split('\t')
+        assert len(parts) == 3
+
+        cases.append(tuple(parts))
+
+    return cases
+
+class DiscoverTestCase(unittest.TestCase):
+    def __init__(self, input_url, normalized_url, xrds_url):
+        unittest.TestCase.__init__(self)
+        self.input_url = input_url
+        self.normalized_url = normalized_url
+        self.xrds_url = xrds_url
+
+    def runTest(self):
+        normalized_url, content = discover(self.input_url)
+
+        msg = 'Identity URL mismatch: actual = %r, expected = %r' % (
+            normalized_url, self.normalized_url)
+        self.failUnlessEqual(self.normalized_url, normalized_url, msg)
+
+        resp = urllib2.urlopen(self.xrds_url)
+        expected_content = resp.read()
+        resp.close()
+
+        msg = 'XRDS content mismatch: actual = %r, expected = %r' % (
+            content, expected_content)
+        self.failUnlessEqual(expected_content, content, msg)
+
+    def shortDescription(self):
+        return "%s (%s)" % (self.input_url, self.__class__.__module__)
+
+def mkSuite(parsed):
+    test_cases = [DiscoverTestCase(*t) for t in parsed]
+    return unittest.TestSuite(test_cases)
+
+def mkSuiteFromURL(manifest_url):
+    req = urllib2.urlopen(manifest_url)
+    parsed = parseManifest(req)
+    return mkSuite(parsed)
+
+def test(url):
+    suite = mkSuiteFromURL(url)
+    runner = unittest.TextTestRunner()
+    return runner.run(suite)
+
+if __name__ == '__main__':
+    import sys
+    test(sys.argv[1])

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/runtests
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/runtests?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/runtests (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/runtests Wed Oct 11 16:09:11 2006
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+# -*- Python -*-
+
+# Does pyunit *still* not come with a test collector?  What is wrong with
+# these people?  Do I have to require all development environments to have
+# twisted.trial installed?
+
+# TODO:
+#  - make command line options work again
+#    - verbose/quiet
+#    - select specific tests or modules
+
+import unittest
+import sys
+
+def fixpath():
+    import os.path
+    try:
+        d = os.path.dirname(__file__)
+    except NameError:
+        d = os.path.dirname(sys.argv[0])
+    yadisparent = os.path.normpath(os.path.join(d, '..', '..'))
+    if yadisparent not in sys.path:
+        print "putting %s in sys.path" % (yadisparent,)
+        sys.path.insert(0, yadisparent)
+
+
+def run():
+    import test_parsehtml
+    import test_discover
+    import test_accept
+    import test_etxrd
+    import test_xri
+    import test_xrires
+
+    loader = unittest.TestLoader()
+    s = unittest.TestSuite()
+    s.addTest(loader.loadTestsFromModule(test_etxrd))
+    s.addTest(test_parsehtml.loadTests())
+    s.addTest(test_discover.loadTests())
+    s.addTest(loader.loadTestsFromModule(test_discover))
+    s.addTest(loader.loadTestsFromModule(test_xri))
+    s.addTest(test_accept.loadTests())
+
+    runner = unittest.TextTestRunner() # verbosity=2)
+
+    result = runner.run(s)
+
+    return result
+
+if __name__ == '__main__':
+    fixpath()
+    sys.exit(not run().wasSuccessful())

Propchange: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/runtests
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_accept.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_accept.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_accept.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_accept.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,127 @@
+import unittest
+import os.path
+from yadis import accept
+
+def getTestData():
+    """Read the test data off of disk
+
+    () -> [(int, str)]
+    """
+    filename = os.path.join(os.path.dirname(__file__), 'data', 'accept.txt')
+    i = 1
+    lines = []
+    for line in file(filename):
+        lines.append((i, line))
+        i += 1
+    return lines
+
+def chunk(lines):
+    """Return groups of lines separated by whitespace or comments
+
+    [(int, str)] -> [[(int, str)]]
+    """
+    chunks = []
+    chunk = []
+    for lineno, line in lines:
+        stripped = line.strip()
+        if not stripped or stripped[0] == '#':
+            if chunk:
+                chunks.append(chunk)
+                chunk = []
+        else:
+            chunk.append((lineno, stripped))
+
+    if chunk:
+        chunks.append(chunk)
+
+    return chunks
+
+def parseLines(chunk):
+    """Take the given chunk of lines and turn it into a test data dictionary
+
+    [(int, str)] -> {str:(int, str)}
+    """
+    items = {}
+    for (lineno, line) in chunk:
+        header, data = line.split(':', 1)
+        header = header.lower()
+        items[header] = (lineno, data.strip())
+
+    return items
+
+def parseAvailable(available_text):
+    """Parse an Available: line's data
+
+    str -> [str]
+    """
+    return [s.strip() for s in available_text.split(',')]
+
+def parseExpected(expected_text):
+    """Parse an Expected: line's data
+
+    str -> [(str, float)]
+    """
+    expected = []
+    if expected_text:
+        for chunk in expected_text.split(','):
+            chunk = chunk.strip()
+            mtype, qstuff = chunk.split(';')
+            mtype = mtype.strip()
+            assert '/' in mtype
+            qstuff = qstuff.strip()
+            q, qstr = qstuff.split('=')
+            assert q == 'q'
+            qval = float(qstr)
+            expected.append((mtype, qval))
+
+    return expected
+
+class MatchAcceptTest(unittest.TestCase):
+    def __init__(self, descr, accept_header, available, expected):
+        unittest.TestCase.__init__(self)
+        self.accept_header = accept_header
+        self.available = available
+        self.expected = expected
+        self.descr = descr
+
+    def shortDescription(self):
+        return self.descr
+
+    def runTest(self):
+        accepted = accept.parseAcceptHeader(self.accept_header)
+        actual = accept.matchTypes(accepted, self.available)
+        self.failUnlessEqual(self.expected, actual)
+
+def loadTests():
+    lines = getTestData()
+    chunks = chunk(lines)
+    data_sets = map(parseLines, chunks)
+    cases = []
+    for data in data_sets:
+        lnos = []
+        lno, header = data['accept']
+        lnos.append(lno)
+        lno, avail_data = data['available']
+        lnos.append(lno)
+        try:
+            available = parseAvailable(avail_data)
+        except:
+            print 'On line', lno
+            raise
+
+        lno, exp_data = data['expected']
+        lnos.append(lno)
+        try:
+            expected = parseExpected(exp_data)
+        except:
+            print 'On line', lno
+            raise
+
+        descr = 'MatchAcceptTest for lines %r' % (lnos,)
+        case = MatchAcceptTest(descr, header, available, expected)
+        cases.append(case)
+    return unittest.TestSuite(cases)
+
+if __name__ == '__main__':
+    runner = unittest.TextTestRunner()
+    runner.run(loadTests())

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_discover.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_discover.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_discover.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_discover.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+"""Tests for yadis.discover.
+
+@todo: Now that yadis.discover uses urljr.fetchers, we should be able to do
+   tests with a mock fetcher instead of spawning threads with BaseHTTPServer.
+"""
+
+import unittest
+import urlparse
+import re
+
+from yadis.discover import discover, DiscoveryFailure
+
+from urljr import fetchers
+
+import discoverdata
+
+status_header_re = re.compile(r'Status: (\d+) .*?$', re.MULTILINE)
+
+four04_pat = """\
+Content-Type: text/plain
+
+No such file %s
+"""
+
+class QuitServer(Exception): pass
+
+def mkResponse(data):
+    status_mo = status_header_re.match(data)
+    headers_str, body = data.split('\n\n', 1)
+    headers = {}
+    for line in headers_str.split('\n'):
+        k, v = line.split(':', 1)
+        k = k.strip().lower()
+        v = v.strip()
+        headers[k] = v
+    status = int(status_mo.group(1))
+    return fetchers.HTTPResponse(status=status,
+                                 headers=headers,
+                                 body=body)
+
+class TestFetcher(object):
+    def __init__(self, base_url):
+        self.base_url = base_url
+
+    def fetch(self, url, headers, body):
+        current_url = url
+        while True:
+            parsed = urlparse.urlparse(current_url)
+            path = parsed[2][1:]
+            try:
+                data = discoverdata.generateSample(path, self.base_url)
+            except KeyError:
+                return fetchers.HTTPResponse(status=404,
+                                             final_url=current_url,
+                                             headers={},
+                                             body='')
+
+            response = mkResponse(data)
+            if response.status in [301, 302, 303, 307]:
+                current_url = response.headers['location']
+            else:
+                response.final_url = current_url
+                return response
+
+class TestSecondGet(unittest.TestCase):
+    class MockFetcher(object):
+        def __init__(self):
+            self.count = 0
+        def fetch(self, uri, headers=None, body=None):
+            self.count += 1
+            if self.count == 1:
+                headers = {
+                    'X-XRDS-Location'.lower(): 'http://unittest/404',
+                    }
+                return fetchers.HTTPResponse(uri, 200, headers, '')
+            else:
+                return fetchers.HTTPResponse(uri, 404)
+
+    def setUp(self):
+        self.oldfetcher = fetchers.getDefaultFetcher()
+        fetchers.setDefaultFetcher(self.MockFetcher())
+
+    def tearDown(self):
+        fetchers.setDefaultFetcher(self.oldfetcher)
+
+    def test_404(self):
+        uri = "http://something.unittest/"
+        self.failUnlessRaises(DiscoveryFailure, discover, uri)
+
+
+class _TestCase(unittest.TestCase):
+    base_url = 'http://invalid.unittest/'
+
+    def __init__(self, input_name, id_name, result_name, success):
+        self.input_name = input_name
+        self.id_name = id_name
+        self.result_name = result_name
+        self.success = success
+        # Still not quite sure how to best construct these custom tests.
+        # Between python2.3 and python2.4, a patch attached to pyunit.sf.net
+        # bug #469444 got applied which breaks loadTestsFromModule on this
+        # class if it has test_ or runTest methods.  So, kludge to change
+        # the method name.
+        unittest.TestCase.__init__(self, methodName='runCustomTest')
+
+    def setUp(self):
+        fetchers.setDefaultFetcher(TestFetcher(self.base_url),
+                                   wrap_exceptions=False)
+
+        self.input_url, self.expected = discoverdata.generateResult(
+            self.base_url,
+            self.input_name,
+            self.id_name,
+            self.result_name,
+            self.success)
+
+    def tearDown(self):
+        fetchers.setDefaultFetcher(None)
+
+    def runCustomTest(self):
+        if self.expected is DiscoveryFailure:
+            self.failUnlessRaises(DiscoveryFailure,
+                                  discover, self.input_url)
+        else:
+            result = discover(self.input_url)
+            self.failUnlessEqual(self.input_url, result.request_uri)
+
+            msg = 'Identity URL mismatch: actual = %r, expected = %r' % (
+                result.normalized_uri, self.expected.normalized_uri)
+            self.failUnlessEqual(
+                self.expected.normalized_uri, result.normalized_uri, msg)
+
+            msg = 'Content mismatch: actual = %r, expected = %r' % (
+                result.response_text, self.expected.response_text)
+            self.failUnlessEqual(
+                self.expected.response_text, result.response_text, msg)
+
+            expected_keys = self.expected.__dict__.keys()
+            expected_keys.sort()
+            actual_keys = result.__dict__.keys()
+            actual_keys.sort()
+            self.failUnlessEqual(actual_keys, expected_keys)
+
+            for k, exp_v in self.expected.__dict__.items():
+                act_v = result.__dict__.get(k)
+                assert act_v == exp_v, (k, exp_v, act_v)
+
+    def shortDescription(self):
+        try:
+            n = self.input_url
+        except AttributeError:
+            # run before setUp, or if setUp did not complete successfully.
+            n = self.input_name
+        return "%s (%s)" % (
+            n,
+            self.__class__.__module__)
+
+def loadTests():
+    s = unittest.TestSuite()
+    for success, input_name, id_name, result_name in discoverdata.testlist:
+        test = _TestCase(input_name, id_name, result_name, success)
+        s.addTest(test)
+
+    return s
+
+def test():
+    runner = unittest.TextTestRunner()
+    return runner.run(loadTests())
+
+if __name__ == '__main__':
+    test()

Propchange: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_discover.py
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_etxrd.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_etxrd.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_etxrd.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_etxrd.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,177 @@
+import unittest
+from yadis import services, etxrd, xri
+import os.path
+
+def sibpath(one, other, make_absolute=True):
+    if os.path.isabs(other):
+        return other
+    p = os.path.join(os.path.dirname(one), other)
+    if make_absolute:
+        p = os.path.abspath(p)
+    return p
+
+
+XRD_FILE = sibpath(__file__, os.path.join("data", "test1-xrd.xml"))
+NOXRDS_FILE = sibpath(__file__, os.path.join("data", "not-xrds.xml"))
+NOXRD_FILE = sibpath(__file__, os.path.join("data", "no-xrd.xml"))
+
+# None of the namespaces or service URIs below are official (or even
+# sanctioned by the owners of that piece of URL-space)
+
+LID_2_0 = "http://lid.netmesh.org/sso/2.0b5"
+TYPEKEY_1_0 = "http://typekey.com/services/1.0"
+
+def simpleOpenIDTransformer(endpoint):
+    """Function to extract information from an OpenID service element"""
+    if 'http://openid.net/signon/1.0' not in endpoint.type_uris:
+        return None
+
+    delegates = list(endpoint.service_element.findall(
+        '{http://openid.net/xmlns/1.0}Delegate'))
+    assert len(delegates) == 1
+    delegate = delegates[0].text
+    return (endpoint.uri, delegate)
+
+class TestServiceParser(unittest.TestCase):
+    def setUp(self):
+        self.xmldoc = file(XRD_FILE).read()
+        self.yadis_url = 'http://unittest.url/'
+
+    def _getServices(self, flt=None):
+        return list(services.applyFilter(self.yadis_url, self.xmldoc, flt))
+
+    def testParse(self):
+        """Make sure that parsing succeeds at all"""
+        services = self._getServices()
+
+    def testParseOpenID(self):
+        """Parse for OpenID services with a transformer function"""
+        services = self._getServices(simpleOpenIDTransformer)
+
+        expectedServices = [
+            ("http://www.myopenid.com/server", "http://josh.myopenid.com/"),
+            ("http://www.schtuff.com/openid", "http://users.schtuff.com/josh"),
+            ("http://www.livejournal.com/openid/server.bml",
+             "http://www.livejournal.com/users/nedthealpaca/"),
+            ]
+
+        it = iter(services)
+        for (server_url, delegate) in expectedServices:
+            for (actual_url, actual_delegate) in it:
+                self.failUnlessEqual(server_url, actual_url)
+                self.failUnlessEqual(delegate, actual_delegate)
+                break
+            else:
+                self.fail('Not enough services found')
+
+    def _checkServices(self, expectedServices):
+        """Check to make sure that the expected services are found in
+        that order in the parsed document."""
+        it = iter(self._getServices())
+        for (type_uri, uri) in expectedServices:
+            for service in it:
+                if type_uri in service.type_uris:
+                    self.failUnlessEqual(service.uri, uri)
+                    break
+            else:
+                self.fail('Did not find %r service' % (type_uri,))
+
+    def testGetSeveral(self):
+        """Get some services in order"""
+        expectedServices = [
+            # type, URL
+            (TYPEKEY_1_0, None),
+            (LID_2_0, "http://mylid.net/josh"),
+            ]
+
+        self._checkServices(expectedServices)
+
+    def testGetSeveralForOne(self):
+        """Getting services for one Service with several Type elements."""
+        types = [ 'http://lid.netmesh.org/sso/2.0b5'
+                , 'http://lid.netmesh.org/2.0b5'
+                ]
+
+        uri = "http://mylid.net/josh"
+
+        for service in self._getServices():
+            if service.uri == uri:
+                found_types = service.matchTypes(types)
+                if found_types == types:
+                    break
+        else:
+            self.fail('Did not find service with expected types and uris')
+
+    def testNoXRDS(self):
+        """Make sure that we get an exception when an XRDS element is
+        not present"""
+        self.xmldoc = file(NOXRDS_FILE).read()
+        self.failUnlessRaises(
+            etxrd.XRDSError,
+            services.applyFilter, self.yadis_url, self.xmldoc, None)
+
+    def testEmpty(self):
+        """Make sure that we get an exception when an XRDS element is
+        not present"""
+        self.xmldoc = ''
+        self.failUnlessRaises(
+            etxrd.XRDSError,
+            services.applyFilter, self.yadis_url, self.xmldoc, None)
+
+    def testNoXRD(self):
+        """Make sure that we get an exception when there is no XRD
+        element present."""
+        self.xmldoc = file(NOXRD_FILE).read()
+        self.failUnlessRaises(
+            etxrd.XRDSError,
+            services.applyFilter, self.yadis_url, self.xmldoc, None)
+
+
+class TestCanonicalID(unittest.TestCase):
+
+    canonicalIDtests = [
+        ("@ootao*test1", "delegated-20060809.xrds",
+         "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+        ("@ootao*test1", "delegated-20060809-r1.xrds",
+         "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+        ("@ootao*test1", "delegated-20060809-r2.xrds",
+         "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+        ("@ootao*test1", "sometimesprefix.xrds",
+         "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+        ("@ootao*test1", "prefixsometimes.xrds",
+         "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+        ("=keturn*isDrummond", "spoof1.xrds", etxrd.XRDSFraud),
+        ("=keturn*isDrummond", "spoof2.xrds", etxrd.XRDSFraud),
+        ("@keturn*is*drummond", "spoof3.xrds", etxrd.XRDSFraud),
+        ("=x", "status222.xrds", None),
+        # Don't let IRI authorities be canonical for the GCS.
+        ("phreak.example.com", "delegated-20060809-r2.xrds", etxrd.XRDSFraud),
+        # TODO: Refs
+        # ("@ootao*test.ref", "ref.xrds", "@!BAE.A650.823B.2475")
+        ]
+
+    # TODO: Add a IRI authority with an IRI canonicalID.
+    # TODO: Add test cases with real examples of multiple CanonicalIDs
+    #   somewhere in the resolution chain.
+
+    def test_getCanonicalID(self):
+        for iname, filename, expectedID in self.canonicalIDtests:
+            filename = sibpath(__file__, os.path.join("data", filename))
+            xrds = etxrd.parseXRDS(file(filename).read())
+            self._getCanonicalID(iname, xrds, expectedID)
+
+
+    def _getCanonicalID(self, iname, xrds, expectedID):
+        if isinstance(expectedID, (str, unicode, type(None))):
+            cid = etxrd.getCanonicalID(iname, xrds)
+            self.failUnlessEqual(cid, expectedID and xri.XRI(expectedID))
+        elif issubclass(expectedID, etxrd.XRDSError):
+            self.failUnlessRaises(expectedID, etxrd.getCanonicalID,
+                                  iname, xrds)
+        else:
+            self.fail("Don't know how to test for expected value %r"
+                      % (expectedID,))
+
+
+if __name__ == '__main__':
+    unittest.main()

Added: incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_parsehtml.py
URL: http://svn.apache.org/viewvc/incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_parsehtml.py?view=auto&rev=463036
==============================================================================
--- incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_parsehtml.py (added)
+++ incubator/heraldry/libraries/python/yadis/trunk/yadis/test/test_parsehtml.py Wed Oct 11 16:09:11 2006
@@ -0,0 +1,82 @@
+from yadis.parsehtml import YadisHTMLParser, ParseDone
+from HTMLParser import HTMLParseError
+
+import os.path, unittest, sys
+
+class _TestCase(unittest.TestCase):
+    reserved_values = ['None', 'EOF']
+
+    def __init__(self, filename, testname, expected, case):
+        self.filename = filename
+        self.testname = testname
+        self.expected = expected
+        self.case = case
+        unittest.TestCase.__init__(self)
+
+    def runTest(self):
+        p = YadisHTMLParser()
+        try:
+            p.feed(self.case)
+        except ParseDone, why:
+            found = why[0]
+
+            # make sure we protect outselves against accidental bogus
+            # test cases
+            assert found not in self.reserved_values
+
+            # convert to a string
+            if found is None:
+                found = 'None'
+
+            msg = "%r != %r for case %s" % (found, self.expected, self.case)
+            self.failUnlessEqual(found, self.expected, msg)
+        except HTMLParseError:
+            assert self.expected == 'None'
+        else:
+            self.failUnless(self.expected == 'EOF', (self.case, self.expected))
+
+    def shortDescription(self):
+        return "%s (%s<%s>)" % (
+            self.testname,
+            self.__class__.__module__,
+            os.path.basename(self.filename))
+
+def parseCases(data):
+    cases = []
+    for chunk in data.split('\f\n'):
+        expected, case = chunk.split('\n', 1)
+        cases.append((expected, case))
+    return cases
+
+def loadTests():
+    """Make a pyunit TestSuite from a file defining test cases."""
+    s = unittest.TestSuite()
+    for (filename, test_num, expected, case) in getCases():
+        s.addTest(_TestCase(filename, str(test_num), expected, case))
+    return s
+
+def test():
+    runner = unittest.TextTestRunner()
+    return runner.run(loadTests())
+
+filenames = ['data/test1-parsehtml.txt']
+
+default_test_files = []
+base = os.path.dirname(__file__)
+for filename in filenames:
+    full_name = os.path.join(base, filename)
+    default_test_files.append(full_name)
+
+def getCases(test_files=default_test_files):
+    cases = []
+    for filename in test_files:
+        test_num = 0
+        data = file(filename).read()
+        for expected, case in parseCases(data):
+            test_num += 1
+            cases.append((filename, test_num, expected, case))
+    return cases
+
+
+if __name__ == '__main__':
+    sys.exit(not test().wasSuccessful())



Mime
View raw message