# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.
#
# Authors:
#   Benjamin Kampmann <benjamin@fluendo.com>

"""
Test the daap parser
"""

from elisa.plugins.daap.daap_parser import DaapParser, NotEnoughData
from elisa.plugins.daap.models import DaapServerInfoModel, \
        DaapSongListModel

from twisted.web2 import responsecode
from twisted.trial.unittest import TestCase

class TestDaapParsing(TestCase):
  
    # for test cases we want to set up a certain parsing. In reality this is
    # created by the /content-codes request.

    def setUp(self):
        # these tests don't need an initialized parser using dbus so we only
        # instantiate it
        self.parser = DaapParser()

    def test_full_chunk_parse(self):
        """
        test parsing of the full chunk (with server info example data)
        """
        data = 'msrv\x00\x00\x00\xb0mstt\x00\x00\x00\x04\x00\x00\x00\xc8mpro\x00\x00\x00\x04\x00\x02\x00\x00apro\x00\x00\x00\x04\x00\x03\x00\x00minm\x00\x00\x00\x12bens Musiqueeeeeeemsau\x00\x00\x00\x01\x00mslr\x00\x00\x00\x01\x00mstm\x00\x00\x00\x04\x00\x00\x07\x08msal\x00\x00\x00\x01\x00msup\x00\x00\x00\x01\x00mspi\x00\x00\x00\x01\x00msex\x00\x00\x00\x01\x00msbr\x00\x00\x00\x01\x00msqy\x00\x00\x00\x01\x00msix\x00\x00\x00\x01\x00msrs\x00\x00\x00\x01\x00msdc\x00\x00\x00\x04\x00\x00\x00\x01'
        
        expected = {'msix': 0, 'msau': 0, 'mslr': 0, 'mstt': 200, 
                    'mspi': 0, 'apro': 3.0, 'msbr': 0, 'mpro': 2.0,
                    'msup': 0, 'mstm': 1800, 'msdc': 1, 'msex': 0,
                    'msqy': 0, 'msal': 0, 'msrs': 0, 'minm': 'bens Musiqueeeeeee'}

        key, rest, nothing = self.parser.parse_chunk(data)
        result = {}
        while(rest):
                key, value, rest = self.parser.parse_chunk(rest)
                result[key] = value

        self.assertEquals(result, expected)

    def test_simple_parse(self):
        """
        test simple parse (with server info example data)
        """
        data = 'msrv\x00\x00\x00\xb0mstt\x00\x00\x00\x04\x00\x00\x00\xc8mpro\x00\x00\x00\x04\x00\x02\x00\x00apro\x00\x00\x00\x04\x00\x03\x00\x00minm\x00\x00\x00\x12bens Musiqueeeeeeemsau\x00\x00\x00\x01\x00mslr\x00\x00\x00\x01\x00mstm\x00\x00\x00\x04\x00\x00\x07\x08msal\x00\x00\x00\x01\x00msup\x00\x00\x00\x01\x00mspi\x00\x00\x00\x01\x00msex\x00\x00\x00\x01\x00msbr\x00\x00\x00\x01\x00msqy\x00\x00\x00\x01\x00msix\x00\x00\x00\x01\x00msrs\x00\x00\x00\x01\x00msdc\x00\x00\x00\x04\x00\x00\x00\x01'
        
        expected = {'msix': 0, 'msau': 0, 'mslr': 0, 'mstt': 200, 
                    'mspi': 0, 'apro': 3.0, 'msbr': 0, 'mpro': 2.0,
                    'msup': 0, 'mstm': 1800, 'msdc': 1, 'msex': 0,
                    'msqy': 0, 'msal': 0, 'msrs': 0, 'minm': 'bens Musiqueeeeeee'}

        result = self.parser.simple_parse(data)
        self.assertEquals(result, expected)


    def test_server_info_to_model(self):
        """
        the server info is one of the first requests, it has to work
        """
        data = 'msrv\x00\x00\x00\xb0mstt\x00\x00\x00\x04\x00\x00\x00\xc8mpro\x00\x00\x00\x04\x00\x02\x00\x00apro\x00\x00\x00\x04\x00\x03\x00\x00minm\x00\x00\x00\x12bens Musiqueeeeeeemsau\x00\x00\x00\x01\x00mslr\x00\x00\x00\x01\x00mstm\x00\x00\x00\x04\x00\x00\x07\x08msal\x00\x00\x00\x01\x00msup\x00\x00\x00\x01\x00mspi\x00\x00\x00\x01\x00msex\x00\x00\x00\x01\x00msbr\x00\x00\x00\x01\x00msqy\x00\x00\x00\x01\x00msix\x00\x00\x00\x01\x00msrs\x00\x00\x00\x01\x00msdc\x00\x00\x00\x04\x00\x00\x00\x01'
        
        expected = {'msix': 0, 'mslr': 0, 'mstt': 200, 
                    'mspi': 0, 'apro': 3.0, 'msbr': 0, 'mpro': 2.0,
                    'msup': 0, 'mstm': 1800, 'msdc': 1, 'msex': 0,
                    'msqy': 0, 'msal': 0, 'msrs': 0, 'minm': 'bens Musiqueeeeeee'}

        key, value, nothing = self.parser.parse_chunk(data)

        model = DaapServerInfoModel()

        def parsed(result):
            for key, value in expected.iteritems():
                name = model.mappings.get(key, None)
                self.assertEquals(getattr(model, name), value)

        dfr = self.parser.parse_to_model(value, model)
        dfr.addCallback(parsed)
        return dfr

    def test_content_codes(self):
        """
        test parsing of example content codes
        """

        data = "mccr\x00\x00\x13 mstt\x00\x00\x00\x04\x00\x00\x00\xc8mdcl\x00\x00\x00-mcnm\x00\x00\x00\x04mdclmcna\x00\x00\x00\x0fdmap.dictionarymcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00)mcnm\x00\x00\x00\x04msttmcna\x00\x00\x00\x0bdmap.statusmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00)mcnm\x00\x00\x00\x04miidmcna\x00\x00\x00\x0bdmap.itemidmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00+mcnm\x00\x00\x00\x04minmmcna\x00\x00\x00\rdmap.itemnamemcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00+mcnm\x00\x00\x00\x04mikdmcna\x00\x00\x00\rdmap.itemkindmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04mpermcna\x00\x00\x00\x11dmap.persistentidmcty\x00\x00\x00\x02\x00\x07mdcl\x00\x00\x00,mcnm\x00\x00\x00\x04mconmcna\x00\x00\x00\x0edmap.containermcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x002mcnm\x00\x00\x00\x04mctimcna\x00\x00\x00\x14dmap.containeritemidmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x004mcnm\x00\x00\x00\x04mpcomcna\x00\x00\x00\x16dmap.parentcontaineridmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04mstsmcna\x00\x00\x00\x11dmap.statusstringmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00,mcnm\x00\x00\x00\x04mimcmcna\x00\x00\x00\x0edmap.itemcountmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x001mcnm\x00\x00\x00\x04mctcmcna\x00\x00\x00\x13dmap.containercountmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x000mcnm\x00\x00\x00\x04mrcomcna\x00\x00\x00\x12dmap.returnedcountmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x006mcnm\x00\x00\x00\x04mtcomcna\x00\x00\x00\x18dmap.specifiedtotalcountmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00*mcnm\x00\x00\x00\x04mlclmcna\x00\x00\x00\x0cdmap.listingmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00.mcnm\x00\x00\x00\x04mlitmcna\x00\x00\x00\x10dmap.listingitemmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00&mcnm\x00\x00\x00\x04mbclmcna\x00\x00\x00\x08dmap.bagmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x005mcnm\x00\x00\x00\x04msrvmcna\x00\x00\x00\x17dmap.serverinforesponsemcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x007mcnm\x00\x00\x00\x04msaumcna\x00\x00\x00\x19dmap.authenticationmethodmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x000mcnm\x00\x00\x00\x04mslrmcna\x00\x00\x00\x12dmap.loginrequiredmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x002mcnm\x00\x00\x00\x04mpromcna\x00\x00\x00\x14dmap.protocolversionmcty\x00\x00\x00\x02\x00\x0bmdcl\x00\x00\x002mcnm\x00\x00\x00\x04apromcna\x00\x00\x00\x14daap.protocolversionmcty\x00\x00\x00\x02\x00\x0bmdcl\x00\x00\x005mcnm\x00\x00\x00\x04msalmcna\x00\x00\x00\x17dmap.supportsautologoutmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x001mcnm\x00\x00\x00\x04msupmcna\x00\x00\x00\x13dmap.supportsupdatemcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x007mcnm\x00\x00\x00\x04mspimcna\x00\x00\x00\x19dmap.supportspersistenidsmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x005mcnm\x00\x00\x00\x04msexmcna\x00\x00\x00\x17dmap.supportsextensionsmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x001mcnm\x00\x00\x00\x04msbrmcna\x00\x00\x00\x13dmap.supportsbrowsemcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x000mcnm\x00\x00\x00\x04msqymcna\x00\x00\x00\x12dmap.supportsquerymcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x000mcnm\x00\x00\x00\x04msixmcna\x00\x00\x00\x12dmap.supportsindexmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x002mcnm\x00\x00\x00\x04msrsmcna\x00\x00\x00\x14dmap.supportsresolvemcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x002mcnm\x00\x00\x00\x04mstmmcna\x00\x00\x00\x14dmap.timeoutintervalmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x001mcnm\x00\x00\x00\x04msdcmcna\x00\x00\x00\x13dmap.databasescountmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x007mcnm\x00\x00\x00\x04mccrmcna\x00\x00\x00\x19dmap.contentcodesresponsemcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x005mcnm\x00\x00\x00\x04mcnmmcna\x00\x00\x00\x17dmap.contentcodesnumbermcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x003mcnm\x00\x00\x00\x04mcnamcna\x00\x00\x00\x15dmap.contentcodesnamemcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x003mcnm\x00\x00\x00\x04mctymcna\x00\x00\x00\x15dmap.contentcodestypemcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x000mcnm\x00\x00\x00\x04mlogmcna\x00\x00\x00\x12dmap.loginresponsemcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00,mcnm\x00\x00\x00\x04mlidmcna\x00\x00\x00\x0edmap.sessionidmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x001mcnm\x00\x00\x00\x04mupdmcna\x00\x00\x00\x13dmap.updateresponsemcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x001mcnm\x00\x00\x00\x04musrmcna\x00\x00\x00\x13dmap.serverrevisionmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00-mcnm\x00\x00\x00\x04mutymcna\x00\x00\x00\x0fdmap.updatetypemcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x003mcnm\x00\x00\x00\x04mudlmcna\x00\x00\x00\x15dmap.deletedidlistingmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x002mcnm\x00\x00\x00\x04avdbmcna\x00\x00\x00\x14daap.serverdatabasesmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x001mcnm\x00\x00\x00\x04abromcna\x00\x00\x00\x13daap.databasebrowsemcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x005mcnm\x00\x00\x00\x04abalmcna\x00\x00\x00\x17daap.browsealbumlistingmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x006mcnm\x00\x00\x00\x04abarmcna\x00\x00\x00\x18daap.browseartistlistingmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x008mcnm\x00\x00\x00\x04abcpmcna\x00\x00\x00\x1adaap.browsecomposerlistingmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x005mcnm\x00\x00\x00\x04abgnmcna\x00\x00\x00\x17daap.browsegenrelistingmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x006mcnm\x00\x00\x00\x04adbsmcna\x00\x00\x00\x18daap.returndatabasesongsmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00,mcnm\x00\x00\x00\x04asalmcna\x00\x00\x00\x0edaap.songalbummcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00-mcnm\x00\x00\x00\x04asarmcna\x00\x00\x00\x0fdaap.songartistmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x006mcnm\x00\x00\x00\x04asbtmcna\x00\x00\x00\x18daap.songsbeatsperminutemcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x00.mcnm\x00\x00\x00\x04asbrmcna\x00\x00\x00\x10daap.songbitratemcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x00.mcnm\x00\x00\x00\x04ascmmcna\x00\x00\x00\x10daap.songcommentmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x002mcnm\x00\x00\x00\x04ascomcna\x00\x00\x00\x14daap.songcompliationmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x000mcnm\x00\x00\x00\x04asdamcna\x00\x00\x00\x12daap.songdateaddedmcty\x00\x00\x00\x02\x00\nmdcl\x00\x00\x003mcnm\x00\x00\x00\x04asdmmcna\x00\x00\x00\x15daap.songdatemodifiedmcty\x00\x00\x00\x02\x00\nmdcl\x00\x00\x000mcnm\x00\x00\x00\x04asdcmcna\x00\x00\x00\x12daap.songdisccountmcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x001mcnm\x00\x00\x00\x04asdnmcna\x00\x00\x00\x13daap.songdiscnumbermcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04asdbmcna\x00\x00\x00\x11daap.songdisabledmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04aseqmcna\x00\x00\x00\x11daap.songeqpresetmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00-mcnm\x00\x00\x00\x04asfmmcna\x00\x00\x00\x0fdaap.songformatmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00,mcnm\x00\x00\x00\x04asgnmcna\x00\x00\x00\x0edaap.songgenremcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x002mcnm\x00\x00\x00\x04asdtmcna\x00\x00\x00\x14daap.songdescriptionmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x005mcnm\x00\x00\x00\x04asrvmcna\x00\x00\x00\x17daap.songrelativevolumemcty\x00\x00\x00\x02\x00\x02mdcl\x00\x00\x001mcnm\x00\x00\x00\x04assrmcna\x00\x00\x00\x13daap.songsampleratemcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00+mcnm\x00\x00\x00\x04asszmcna\x00\x00\x00\rdaap.songsizemcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x000mcnm\x00\x00\x00\x04asstmcna\x00\x00\x00\x12daap.songstarttimemcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04asspmcna\x00\x00\x00\x11daap.songstoptimemcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x00+mcnm\x00\x00\x00\x04astmmcna\x00\x00\x00\rdaap.songtimemcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x001mcnm\x00\x00\x00\x04astcmcna\x00\x00\x00\x13daap.songtrackcountmcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x002mcnm\x00\x00\x00\x04astnmcna\x00\x00\x00\x14daap.songtracknumbermcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x001mcnm\x00\x00\x00\x04asurmcna\x00\x00\x00\x13daap.songuserratingmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x00+mcnm\x00\x00\x00\x04asyrmcna\x00\x00\x00\rdaap.songyearmcty\x00\x00\x00\x02\x00\x03mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04asdkmcna\x00\x00\x00\x11daap.songdatakindmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x00.mcnm\x00\x00\x00\x04asulmcna\x00\x00\x00\x10daap.songdataurlmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x004mcnm\x00\x00\x00\x04aplymcna\x00\x00\x00\x16daap.databaseplaylistsmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00/mcnm\x00\x00\x00\x04abplmcna\x00\x00\x00\x11daap.baseplaylistmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x000mcnm\x00\x00\x00\x04apsomcna\x00\x00\x00\x12daap.playlistsongsmcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00*mcnm\x00\x00\x00\x04prsvmcna\x00\x00\x00\x0cdaap.resolvemcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00.mcnm\x00\x00\x00\x04arifmcna\x00\x00\x00\x10daap.resolveinfomcty\x00\x00\x00\x02\x00\x0cmdcl\x00\x00\x00Emcnm\x00\x00\x00\x04aesvmcna\x00\x00\x00'com.applie.itunes.music-sharing-versionmcty\x00\x00\x00\x02\x00\x05mdcl\x00\x00\x009mcnm\x00\x00\x00\x04msasmcna\x00\x00\x00\x1bdaap.authentication.schemesmcty\x00\x00\x00\x02\x00\x01mdcl\x00\x00\x00/mcnm\x00\x00\x00\x04agrpmcna\x00\x00\x00\x11daap.songgroupingmcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00/mcnm\x00\x00\x00\x04ascpmcna\x00\x00\x00\x11daap.songcomposermcty\x00\x00\x00\x02\x00\tmdcl\x00\x00\x00=mcnm\x00\x00\x00\x04aespmcna\x00\x00\x00\x1fcom.apple.itunes.smart-playlistmcty\x00\x00\x00\x02\x00\x01"
        
        expected_result ={'aply': 'daap.databaseplaylists', 'abal':
        'daap.browsealbumlisting', 'abar': 'daap.browseartistlisting', 'adbs':
        'daap.returndatabasesongs', 'msdc': 'dmap.databasescount', 'muty':
        'dmap.updatetype', 'mcna': 'dmap.contentcodesname', 'arif':
        'daap.resolveinfo', 'mcnm': 'dmap.contentcodesnumber', 'abro':
        'daap.databasebrowse', 'mpco': 'dmap.parentcontainerid', 'mlit':
        'dmap.listingitem', 'mbcl': 'dmap.bag', 'mlid': 'dmap.sessionid',
        'mcti': 'dmap.containeritemid', 'aesv':
        'com.applie.itunes.music-sharing-version', 'asfm': 'daap.songformat',
        'mctc': 'dmap.containercount', 'mcty': 'dmap.contentcodestype', 'mdcl':
        'dmap.dictionary', 'asbr': 'daap.songbitrate', 'mtco':
        'dmap.specifiedtotalcount', 'msau': 'dmap.authenticationmethod', 'mrco':
        'dmap.returnedcount', 'msix': 'dmap.supportsindex', 'mpro':
        'dmap.protocolversion', 'abpl': 'daap.baseplaylist', 'asrv':
        'daap.songrelativevolume', 'mper': 'dmap.persistentid', 'msqy':
        'dmap.supportsquery', 'asar': 'daap.songartist', 'mlcl': 'dmap.listing',
        'mimc': 'dmap.itemcount', 'asal': 'daap.songalbum', 'astm':
        'daap.songtime', 'mcon': 'dmap.container', 'mspi':
        'dmap.supportspersistenids', 'asyr': 'daap.songyear', 'msrs':
        'dmap.supportsresolve', 'mudl': 'dmap.deletedidlisting', 'msas':
        'daap.authentication.schemes', 'mstt': 'dmap.status', 'asdt':
        'daap.songdescription', 'agrp': 'daap.songgrouping', 'msts':
        'dmap.statusstring', 'mstm': 'dmap.timeoutinterval', 'mupd':
        'dmap.updateresponse', 'asdk': 'daap.songdatakind', 'asdm':
        'daap.songdatemodified', 'asdn': 'daap.songdiscnumber', 'aesp':
        'com.apple.itunes.smart-playlist', 'asda': 'daap.songdateadded', 'asdb':
        'daap.songdisabled', 'asdc': 'daap.songdisccount', 'abgn':
        'daap.browsegenrelisting', 'astn': 'daap.songtracknumber', 'astc':
        'daap.songtrackcount', 'asbt': 'daap.songsbeatsperminute', 'mikd':
        'dmap.itemkind', 'msbr': 'dmap.supportsbrowse', 'ascp':
        'daap.songcomposer', 'ascm': 'daap.songcomment', 'asco':
        'daap.songcompliation', 'musr': 'dmap.serverrevision', 'asgn':
        'daap.songgenre', 'assz': 'daap.songsize', 'msrv':
        'dmap.serverinforesponse', 'asst': 'daap.songstarttime', 'assp':
        'daap.songstoptime', 'assr': 'daap.songsamplerate', 'minm':
        'dmap.itemname', 'apro': 'daap.protocolversion', 'prsv': 'daap.resolve',
        'abcp': 'daap.browsecomposerlisting', 'msex': 'dmap.supportsextensions',
        'mlog': 'dmap.loginresponse', 'msal': 'dmap.supportsautologout', 'miid':
        'dmap.itemid', 'aseq': 'daap.songeqpreset', 'msup':
        'dmap.supportsupdate', 'mccr': 'dmap.contentcodesresponse', 'mslr':
        'dmap.loginrequired', 'asul': 'daap.songdataurl', 'apso':
        'daap.playlistsongs', 'avdb': 'daap.serverdatabases', 'asur':
        'daap.songuserrating'}

        # check that they got added properly
        expected_codes = {'aply': 'c', 'abal': 'c', 'abar': 'c', 'adbs': 'c',
        'muty': 'b', 'mcna': 's', 'arif': 'c', 'mcnm': 's', 'abro': 'c', 'mpco':
        'i', 'mlit': 'c', 'mbcl': 'c', 'mlid': 'i', 'mcti': 'i', 'aesv': 'i',
        'asfm': 's', 'mctc': 'i', 'mcty': 'h', 'mdcl': 'c', 'asbr': 'h', 'mtco':
        'i', 'msau': 'b', 'msal': 'b', 'msix': 'b', 'mpro': 'version', 'abpl':
        'b', 'asrv': 'B', 'mper': 'q', 'msqy': 'b', 'asar': 's', 'mlcl': 'c',
        'mimc': 'i', 'asal': 's', 'astm': 'i', 'mcon': 'c', 'mspi': 'b', 'asyr':
        'h', 'msrs': 'b', 'mudl': 'c', 'msas': 'b', 'msup': 'b', 'mstt': 'i',
        'asdt': 's', 'agrp': 's', 'msts': 's', 'mstm': 'i', 'mupd': 'c', 'asdk':
        'b', 'asdm': 'I', 'asdn': 'h', 'aesp': 'b', 'asda': 'I', 'asdb': 'b',
        'asdc': 'h', 'abgn': 'c', 'astn': 'h', 'astc': 'h', 'msst': 'i', 'asbt':
        'h', 'mikd': 'b', 'msbr': 'b', 'ascp': 's', 'ascm': 's', 'asco': 'b',
        'musr': 'i', 'asgn': 's', 'assz': 'i', 'msrv': 'c', 'asst': 'i', 'assp':
        'i', 'assr': 'i', 'minm': 's', 'apro': 'version', 'prsv': 'c', 'abcp':
        'c', 'msex': 'b', 'mlog': 'c', 'mrco': 'i', 'miid': 'i', 'aseq': 's',
        'msdc': 'i', 'mccr': 'c', 'msub': 'b', 'mslr': 'b', 'asul': 's', 'apso':
        'c', 'avdb': 'c', 'asur': 'b'}

        key, rest, nothing = self.parser.parse_chunk(data)
        result = {}
        while(rest):
                key, value, rest = self.parser.parse_chunk(rest)
                if key == 'mdcl':
                    mcna, mcnm, mcty = self.parser.parse_mdcl(value)
                    result[mcna] = mcnm
                else:
                    result[key] = value

        self.assertEquals(result, expected_result)
        self.assertEquals(self.parser._code_to_type, expected_codes)
        

    def test_login_data(self):
        """
        try to parse the login data
        """
        data = 'mlog\x00\x00\x00\x18mstt\x00\x00\x00\x04\x00\x00\x00\xc8mlid\x00\x00\x00\x04\x88\xd7>\xbe'
        
        code, value, nothing = self.parser.parse_chunk(data)
        self.assertEquals(code, 'mlog')

        mstt, value, rest = self.parser.parse_chunk(value)
        self.assertEquals(mstt, 'mstt')
        self.assertEquals(value, 200)

        mlid, value, nothing = self.parser.parse_chunk(rest)
        self.assertEquals(mlid, 'mlid')
        self.assertEquals(value, -1999159618)

    def test_fail_on_to_less_data(self):
        """
        too less data raises the expected exception
        """
        data = 'mlog\x00\x00\x00\x18mstt\x00\x00\x00\x04\x00\x00\x00\xc8mlid\x00'
        self.assertRaises(NotEnoughData, self.parser.parse_chunk, data)

    def test_in_chunk_parsing(self):
        """
        test parsing of data in chunks into a model
        """
        data = ['mstt\x00\x00\x00\x04\x00\x00\x00\xc8mpro\x00\x00\x00\x04\x00\x02',
        '\x00\x00apro\x00\x00\x00\x04\x00\x03\x00\x00minm\x00\x00\x00\x12',
        'bens Musiqueeeeeeemsau\x00\x00\x00\x01\x00mslr\x00\x00\x00\x01',
        '\x00mstm\x00\x00\x00\x04\x00\x00\x07\x08msal\x00\x00\x00\x01\x00m',
        'sup\x00\x00\x00\x01\x00mspi\x00\x00\x00\x01\x00msex\x00\x00\x00',
        '\x01\x00msbr\x00\x00\x00\x01\x00msqy\x00\x00\x00\x01\x00',
        'msix\x00\x00\x00\x01\x00msrs\x00\x00\x00\x01\x00msdc\x00',
        '\x00\x00\x04\x00\x00\x00\x01']
        
        expected = {'msix': 0, 'mslr': 0, 'mstt': 200, 
                    'mspi': 0, 'apro': 3.0, 'msbr': 0, 'mpro': 2.0,
                    'msup': 0, 'mstm': 1800, 'msdc': 1, 'msex': 0,
                    'msqy': 0, 'msal': 0, 'msrs': 0, 'minm': 'bens Musiqueeeeeee'}

        model = DaapServerInfoModel()
        rest = ''
        for line in data:
            value = rest + line
            rest = [d for d in self.parser._parse_to_model(value, model)][-1]

        for key, value in expected.iteritems():
            name = model.mappings.get(key, None)
            self.assertEquals(getattr(model, name), value)

    def test_with_container_parsing_in_chunks(self):
        """
        try to parse data with containers into models and doing this in chunks (
        the hardest part)
        """
        data = """mstt\x00\x00\x00\x04\x00\x00\x00\xc8muty\x00\x00\x00\x01\x00mtco\x00\x00\x00\x04\x00\x00\x10\x82mrco\x00\x00\x00\x04\x00\x00\x10\x82mlcl\x00\t\x92\xe1mlit\x00\x00\x00\xb7mikd\x00\x00\x00\x01\x02miid\x00\x00\x00\x04\x00\x00\x00\x01minm\x00\x00\x00#Do You Remember Rock 'n' Roll Radioasal\x00\x00\x00.We're a Happy Family: A Tribute to the Ramonesasar\x00\x00\x00\x04Kissasgn\x00\x00\x00\x0bHeavy Metalassz\x00\x00\x00\x04\x00N\xc0\x00astm\x00\x00\x00\x04\x00\x03C\xf0astn\x00\x00\x00\x02\x00\x06mlit\x00\x00\x00\x92mikd\x00\x00\x00\x01\x02miid\x00\x00\x00\x04\x00\x00\x00\x02minm\x00\x00\x00\x10Live And Let Dieasal\x00\x00\x00\x13Use Your Illusion Iasar\x00\x00\x00\rGuns N' Rosesasgn\x00\x00\x00\x0bHeavy Metalassz\x00\x00\x00\x04\x00-0\x00astm\x00\x00\x00\x04\x00\x02\xce\xc0astn\x00\x00\x00\x02\x00\x03mlit\x00\x00\x00\x9fmikd\x00\x00\x00\x01\x02miid\x00\x00\x00\x04\x00\x00\x00\x03minm\x00\x00\x00\x1cShould I Stay Or Should I Goasal\x00\x00\x00\x14Vivid [Bonus Tracks]asar\x00\x00\x00\rLiving Colourasgn\x00\x00\x00\x0bHeavy Metalassz\x00\x00\x00\x04\x00#\xe0\x00astm\x00\x00\x00\x04\x00\x02:Pastn\x00\x00\x00\x02\x00\rmlit\x00\x00\x00\x8fmikd\x00\x00\x00\x01\x02miid\x00\x00\x00\x04\x00\x00\x00\x04minm\x00\x00\x00\x17Lay All Your Love On Measal\x00\x00\x00\rMetal Jukeboxasar\x00\x00\x00\tHelloweenasgn\x00\x00\x00\x0bHeavy Metalassz\x00\x00\x00\x04\x00C\xc0\x00astm\x00\x00\x00\x04\x00\x046 astn\x00\x00\x00\x02\x00\x03mlit\x00\x00\x00\x90mikd\x00\x00\x00\x01\x02miid\x00\x00\x00\x04\x00\x00\x00\x05minm\x00\x00\x00\x0cGolden Yearsasal\x00\x00\x00\x14From Highway To Hellasar\x00\x00\x00\x0eMarilyn Mansonasgn\x00\x00\x00\x0bHeavy Metalassz\x00\x00\x00\x04\x00R\xb0\x00astm\x00\x00\x00\x04\x00\x03n\xe8astn\x00\x00\x00\x02\x00\x00mlit\x00\x00\x00\x8emikd\x00\x00\x00\x01\x02miid\x00\x00\x00\x04\x00\x00\x00\x06minm\x00\x00\x00\x14No More Mr. Nice Guyasal\x00\x00\x00\x10Hidden Treasuresasar\x00\x00\x00\x08Megadethasgn\x00\x00\x00\x0bHeavy Metalassz\x00\x00\x00\x04"""

        model = DaapSongListModel()
        rest = ''
        while len(data):
            # get the next 40
            # read the next chunk of data
            next, data = data[:40], data[40:]
            value = rest + next
            rest = [d for d in self.parser._parse_to_model(value, model)][-1]

        self.assertEquals(len(model.songs), 5)
        self.assertEquals(model.songs[1].artist, "Guns N' Roses")

