import unittest
from Bunch import Bunch
    
def inc_string(string, allowGrowth=True):
    '''Increment a string.'''
    CHAR_RANGES = [
                   Bunch(from_char=48, to_char=57),  # digits
                   Bunch(from_char=65, to_char=90),  # upper case
                   Bunch(from_char=97, to_char=122), # lower case
                  ]
    string_chars = list(string)
    string_chars[-1] = chr(ord(string_chars[-1]) + 1)
    for index in range(-1, -len(string_chars), -1):
        for char_range in CHAR_RANGES:
            if ord(string_chars[index]) == char_range.to_char + 1:
                string_chars[index] = chr(char_range.from_char)
                string_chars[index-1] = chr(ord(string_chars[index-1]) + 1)
    for char_range in CHAR_RANGES:
        if ord(string_chars[0]) == char_range.to_char + 1:
            if allowGrowth:
                string_chars[0] = chr(char_range.from_char)
                string_chars.insert(0, chr(char_range.from_char))
            else:
                raise ValueError, string + " cannot be incremented."
    return ''.join(string_chars)
            
class Test(unittest.TestCase):
    
    def testSingle(self):
        '''Test increment of a single character.'''
        got = inc_string('a')
        expected = 'b'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testDouble(self):
        '''Test increment of a two character string.'''
        got = inc_string('aa')
        expected = 'ab'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testUpperCaseDouble(self):
        '''Test increment of a two character string.'''
        got = inc_string('AA')
        expected = 'AB'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testDigit(self):
        '''Test increment of a two character string.'''
        got = inc_string('a5')
        expected = 'a6'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testRoll(self):
        '''Test carring increment.'''
        got = inc_string('az')
        expected = 'ba'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testDigitRoll(self):
        '''Test increment of a two character string.'''
        got = inc_string('a9')
        expected = 'b0'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testUpperCaseRoll(self):
        '''Test increment of a two character string.'''
        got = inc_string('A9')
        expected = 'B0'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testMax(self):
        '''Test growing string.'''
        got = inc_string('zz')
        expected = 'aaa'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
            
    def testNoGrowth(self):
        '''Test growing string.'''
        try:
            got = inc_string('zz', allowGrowth=False)
            assert False, "expected ValueError, got '" + str(got) + "'"
        except ValueError:
            # Expected
            pass
    
    def testReal1(self):
        '''Test real world example.'''
        got = inc_string('sbbrkrpctc')
        expected = 'sbbrkrpctd'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
    
    def testReal(self):
        '''Test real world example.'''
        got = inc_string('sbbrkrpct9')
        expected = 'sbbrkrpcu0'
        assert got == expected, "Expected '" + expected + "', got '" + str(got) + "'"
            
if __name__ == '__main__':
    unittest.main()