#!/usr/bin/env python

# Unit tests for the AttrDict module.
# Author: Steven Brown <steven.w.j.brown@gmail.com>
# Homepage: http://stevenbrown.ca
# Copyright (C) 2008 Steven Brown

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Unit tests for attrdict.py.
"""

__version__  = "0.1"


from attrdict import *
import unittest

class AttrDictTestCase(unittest.TestCase):

    keys_values = [
        ('key','value'),
        ('apple',23),
        ('list',[1,2,3]),
        ('tuple',(1,2,3)),
        ('dict',{1:'ok in this dict!',(1,2):'also ok!'}) ]

    
    def setUp(self):
        ad = AttrDict()
        for (k,v) in self.keys_values:
            ad[k] = v
        self.ad = ad

    def tearDown(self):
        pass


class Compatibility(AttrDictTestCase):

    def testInitBasic(self):
        self.assertEqual(len(self.ad.keys()), len(vars(self.ad)))
    
    def testGetItem(self):
        self.assertEqual(self.ad["apple"], 23)

    def testSetItem(self):
        self.ad['testSetItem'] = "Test"
        self.assertEqual(self.ad['testSetItem'], "Test")

    def testGetAttr(self):
        for (k,v) in self.keys_values:
            self.assertEqual(getattr(self.ad, k), v)
        self.assertEqual(self.ad.apple, 23)

    def testSetAttr(self):
        k,v = "testSetAttr","Another Attribute"
        setattr(self.ad, k, v)
        self.assertEqual(self.ad.testSetAttr, v)
        v2 = "Yet Another"
        self.ad.another = v2
        self.assertEqual(self.ad["another"], v2)

    def testRepr(self):
        r = repr(self.ad)
        s = str(self.ad)
        new_ad = eval(r)
        self.assertEqual(type(new_ad), type(self.ad))
        
    def testShortHand(self): #FIXME not needed?
        self.assertEqual(self.ad.key, self.ad['key'])
        self.ad.key2 = 'value2'
        self.assertEqual(self.ad.key2, self.ad['key2'])

    def testDelItem(self):
        before = len(self.ad)
        del self.ad["key"]
        after = len(self.ad)
        self.assert_(before > after)
        self.assert_(before == after+1)
        self.assertRaises(KeyError, self.ad.__getitem__, "key")
        self.assertRaises(KeyError, lambda:self.ad["key"])
        

    def testDelAttr(self):
        before = len(self.ad)
        del self.ad.key
        after = len(self.ad)
        self.assert_(before > after)
        self.assert_(before == after+1)
        self.assertRaises(AttributeError, lambda:self.ad.key)


    def testPop(self):
        self.assertEqual(self.keys_values[1][1],
                         self.ad.pop(self.keys_values[1][0]))
        self.assertRaises(KeyError, self.ad.pop, self.keys_values[1][0])
        self.assertRaises(AttributeError, getattr, self.ad,
                          self.keys_values[1][0])
        self.assertEqual(self.ad.pop(self.keys_values[1][0],None),None)
        self.assertEqual(self.ad.pop(self.keys_values[1][0],'default'),'default')

    
    def testPopitem(self):
        before = len(self.ad)
        item = self.ad.popitem()
        after = len(self.ad)
        self.assertEqual(before, after + 1)
        self.assertEqual(item, self.keys_values[self.keys_values.index(item)])
        self.ad.clear()
        self.assertRaises(KeyError, self.ad.popitem)


        
    def testSetdefault(self):
        self.assertEqual(self.ad.setdefault('missing','a value to set'),
                         'a value to set')
        self.assertEqual(self.ad.missing, 'a value to set')
        self.assertEqual(self.ad.setdefault('missing2'), None) #TODO: dbl check
        self.assertEqual(self.ad.setdefault('key'),'value')
        


    def testGet(self):
        self.assertEqual(self.ad.get('key'),'value')
        self.assertEqual(self.ad.get('not there'),None)
        self.assertEqual(self.ad.get('key',"default"),'value')
        self.assertEqual(self.ad.get('not there',"default"),'default')


    def testUpdate(self):
        d = {'a':1,'b':2,'c':3}
        d_bad = {1:'a',2:'b','3':'c','print':'keyword'}

        ad = AttrDict()
        self.assertRaises(AttributeError,ad.update, d_bad)

        self.ad.update(d)
        self.ad.update(d, z='new')
        self.assertEqual(self.ad.z, 'new')

        before = len(self.ad)
        self.ad.update(t='tuple',y='yes')
        after = len(self.ad)
        self.assertEqual(before, after-2)
        self.assertEqual(self.ad.t, 'tuple')
        self.assertEqual(self.ad['y'], 'yes')

        self.ad.update()
        self.ad.update([('k1','v1'),('k2','v2')])
        d.clear()
        self.ad.update(d)
        self.ad.update(d='d is set!')
        self.assertRaises(TypeError, self.ad.update, {}, {})


    def testClear(self):
        self.ad.clear()
        self.assertEqual(len(self.ad), 0)
        self.assertEqual(len(vars(self.ad)), 0)
    

class BadInput(AttrDictTestCase):

    def testKeyMustBeString(self):
        self.assertRaises(TypeError, self.ad.__setitem__, 23, 23)

    def testAttrNameMustBeString(self):
        self.assertRaises(TypeError, self.ad.__setattr__, 23, 23)

    def testKeyStringSyntax1(self):
        self.assertRaises(AttributeError, self.ad.__setitem__, "a string", 23)

    def testKeyStringSyntax2(self):
        self.assertRaises(AttributeError, self.ad.__setitem__, "23strings", 23)

    def testSetattrKeyword(self):
        self.assertRaises(AttributeError, setattr, self.ad, "print", "keywords are no good as attribute names")

        
class SanityCheck(AttrDictTestCase):

    def testInheritance(self):
        self.assert_(isinstance(self.ad,dict))

    def testSetattrNonString(self):
        self.assertRaises(TypeError, setattr, self.ad, (1,2), "tuples as attribute names? No way.")


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