import YamlTest
from yaml.dump import *
import yaml
from here import flushLeft
from test import assertEquals

class SampleClass:
    def expected(self, modname, classname):
        """
        Tough to test, because this class may be in __main__ 
        namespace, may be in TestDumper, depending where you 
        run it
        """
        return """
        --- !!%s.%s
        bar: 4
        foo: 3
        items:
            - apple
            - banana
        """ % (modname, classname)

    def __init__(self):
        self.foo = 3
        self.bar = 4
        self.items = ['apple', 'banana']


class SampleException(Exception):
    def __init__(self, args):
        self.args = args

class Test(YamlTest.YamlTest):
    def dumpTest(self, obj, expect):
        assertEquals(yaml.dump(obj), flushLeft(expect))
    def dumpTestSort(self,sort):
        obj = {'a':'aaaa','d':'dddd','b':'bbbb','c':'cccc'}
        exp = flushLeft(\
            """
            ---
            b: bbbb
            d: dddd
            a: aaaa
            c: cccc
            """)
        dumper = yaml.Dumper().setSort(sort)
        self.assertEquals(dumper.dump(obj),exp)
    def testsort_map(self):
        self.dumpTestSort({'a':4,'b':2,'d':3})
    def testsort_tuple(self):
        self.dumpTestSort(('b','d','a'))
    def testsort_list(self):
        self.dumpTestSort(['b','d','a'])
    def testsort_func(self):
        self.dumpTestSort({'a':4,'b':2,'d':3}.get)
    def test1(self):
        self.dumpTest(
            { 'foo': 'bar', 'yo': 'mama' },
            """
            ---
            foo: bar
            yo: mama
            """,
        )

    def test2(self):
        self.dumpTest(
            { 'foo': {'bar': 3} },
            """
            ---
            foo:
                bar: 3
            """,
        )

    def test3(self):
        self.dumpTest(
            { 
                'foo': 3,
                'fruits': {
                    'apple': 'red',
                    'banana': 'yellow',
                }
            },
            """
            ---
            foo: 3
            fruits:
                apple: red
                banana: yellow
            """
            )
                
    def test4(self):
        self.dumpTest(
            { 
                'foo': 3,
                'fruits': ['apple', 'orange']
            },
            """
            ---
            foo: 3
            fruits:
                - apple
                - orange
            """
            )
                
    def testTuple(self):
        self.dumpTest(
            { 
                'foo': 3,
                'fruits': ('apple', 'orange')
            },
            """
            ---
            foo: 3
            fruits:
                - apple
                - orange
            """
            )
                
    def testMultiLineBlock(self):
        self.dumpTest(
             { 
                 'foo': "a\nb\nc\n",
             },
             """
             ---
             foo: |
                 a
                 b
                 c
             """
             )

    def testDoubleQuote(self):
        assertEquals(doubleUpQuotes("foo'bar"), "foo''bar")
        assertEquals(doubleUpQuotes("''x'"), "''''x''")

    def testQuotingRules(self):
        # Raw:
        self.assertEquals(dump(None), "--- ~\n")
        self.assertEquals(dump('simple'), "--- simple\n")
        self.assertEquals(dump(''), "--- ''\n")
        self.assertEquals(dump('two words'), "--- two words\n")
        self.assertEquals(dump('single\'quote'), "--- single'quote\n")
        self.assertEquals(dump('5.2'), "--- '5.2'\n")
        self.assertEquals(dump(5.2), "--- 5.2\n")
        self.assertEquals(dump(1234), "--- 1234\n")
        self.assertEquals(dump('harmless-dash'), "--- harmless-dash\n")
        self.assertEquals(dump('* harmless bullet'), "--- * harmless bullet\n")
        self.dumpTest(
            [ "a - b", "another-harmless-dash" ],
            """
            ---
            - a - b
            - another-harmless-dash
            """)
        # Single Quoted:
        self.assertEquals(dump(''), "--- ''\n")
        self.assertEquals(dump('4.3.1.5.2'), "--- '4.3.1.5.2'\n")
        self.assertEquals(dump("'leading quote"), '--- "\'leading quote"\n')
        self.assertEquals(dump('4.3.'), "--- '4.3.'\n")
        self.assertEquals(dump('12345'), "--- '12345'\n")
        self.assertEquals(dump('-12345'), "--- '-12345'\n")
        self.assertEquals(dump('key: value'), "--- 'key: value'\n")
        self.assertEquals(dump('ending colon:'), "--- 'ending colon:'\n")
        self.assertEquals(dump('&foo'), "--- '&foo'\n")
        self.assertEquals(dump(' xx'), "--- ' xx'\n")
        self.assertEquals(dump('*bar'), "--- '*bar'\n")
        assertEquals(dump('"middle \'quote'), '--- \'"middle \'\'quote\'\n')
        # Double quoted:
        self.assertEquals(dump("\thas a tab"), "--- \"\\thas a tab\"\n")
        self.dumpTest(
            { "key: quote": 'normal value' },
            """
            ---
            'key: quote': normal value
            """)

    def testArrayWithSingleQuoted(self):
        self.dumpTest(
            [ 'foo:', {'bar': 'colon:'}],
            """
            ---
            - 'foo:'
            -
                bar: 'colon:'
            """
            )

    def testDumpObject(self):
        sample = SampleClass()
        self.dumpTest(sample, 
            sample.expected(sample.__module__, 'SampleClass'))

    def testWithSpuriousToYaml(self):
        class ClassWithSpuriousToYaml(SampleClass):
            to_yaml = 1 # Shouldn't get invoked
        x = ClassWithSpuriousToYaml()
        self.dumpTest(x, 
            x.expected(x.__module__,'ClassWithSpuriousToYaml'))

    def testNormalToYamlUse(self):
        class NormalYamlUse(SampleClass):
            def to_yaml(self):
                dict = {}
                dict['bar'] = self.foo * 2
                return (dict, None)
        self.dumpTest(NormalYamlUse(),
            """
            ---
            bar: 6
            """)

    def testDumpingPrivateTypes(self):
        class Foobar(SampleClass):
            def to_yaml(self):
                dict = {'foo': 'bar'}
                return (dict, '!!foobar')
        self.dumpTest(Foobar(),
            """
            --- !!foobar
            foo: bar
            """)
        class OddList(SampleClass):
            def to_yaml(self):
                return ([1, 3, 5], '!!oddlist')
        self.dumpTest(OddList(),
            """
            --- !!oddlist
            - 1
            - 3
            - 5
            """)


    def testToYamlShouldBeAllowedToThrowIfItWants(self):
        class ThrowsInsideToYaml:
            def to_yaml(self):
                raise SampleException('bla')
        caught_exception = 'undef'
        try:
            dump(ThrowsInsideToYaml())
        except Exception, e:
            caught_exception = e
        self.assertEquals('bla', caught_exception.args)

    def testObjectWithListOfClasses(self):
        class Foo:
            def __init__(self, items):
                self.items = items
             
        class Bar:
            def __init__(self, data):
                self.data = data

        foo = Foo([Bar('apple'), Bar('banana')])
        mod = foo.__module__ # depends where called
        self.dumpTest(foo,
            """
            --- !!%s.Foo
            items:
                - !!%s.Bar
                    data: apple
                - !!%s.Bar
                    data: banana
            """ % (mod, mod, mod))

    def testHasMethod(self):
        class ClassWithMethodA:
            b = 1
            def a(): pass

        sc = ClassWithMethodA()
        self.assertEquals(1, hasMethod(sc, 'a'))
        self.assertEquals(0, hasMethod(sc, 'b'))

    def testComplexKey(self):
        self.dumpTest( {(3,4): 4},
            """
            ---
            ?
                - 3
                - 4
            : 4
            """)            
    
    def testApostrophe(self):
        self.dumpTest( "Joe's hot dogs.\n",
            """
            --- |
            Joe's hot dogs.
            """)

    def testCustomIndent(self):
        self.assertEquals(
            yaml.Dumper().setIndent('xx').dump(
                { 
                    'foo': 3,
                    'fruits': ('apple', 'orange')
                }, 
            ),
            flushLeft(
                """
                ---
                foo: 3
                fruits:
                xx- apple
                xx- orange
                """
            )
            )

    def testUnicode(self):
        if YamlTest.hasUnicode:
            self.dumpTest({'foo': u"Foo\u263A"},
                r"""
                ---
                foo: "Foo\u263a"
                """)

    def testTab(self):
        self.dumpTest({'tab': "foo\tbar"},
            r"""
            ---
            tab: "foo\tbar"
            """)

    def testIsMulti(self):
        self.failUnless(isMulti("foo\nbar"))
        self.failIf(isMulti("foobar"))
        self.failIf(isMulti("line\twith tab\nsecond line"))
        self.failUnless(isMulti("foo\\slash\nbar"))

    def testHasSpecialChar(self):
        self.failUnless(hasSpecialChar("foo\tbar"))
        self.failIf(hasSpecialChar("foobar"))

    def testTabs(self):
        self.dumpTest({'control': "\b1998\t1999\t2000\n"},
            r"""
            ---
            control: "\b1998\t1999\t2000\n"
            """)

    def test12345(self):
        self.dumpTest({'foo': 12345, 'bar': '12345'},
            r"""
            ---
            bar: '12345'
            foo: 12345
            """)

    def testDataThatsCoincidentallyTheSame(self):
        self.dumpTest([ {'foo': 'bar'}, {'foo': 'bar'} ],
            """
            ---
            -
                foo: bar
            -
                foo: bar
            """)

    def testAliasClass(self):
        dup = {'foo': 'bar'}
        dupList = [dup, dup]
        myAnchors = YamlAnchors(dupList)
        self.assertEquals(id(dup), myAnchors._anchorVisits.keys()[0])
        self.assertEquals(0,myAnchors.isAlias(dup))
        self.assertEquals(1,myAnchors.shouldAnchor(dup) )
        self.assertEquals(0,myAnchors.shouldAnchor('bar'))
        self.assertEquals(0,myAnchors.shouldAnchor(dup['foo']))
        self.assertEquals(0,myAnchors.shouldAnchor(dup))
        self.assertEquals(1,myAnchors.isAlias(dup))

    def testAliases(self):
        dup = {'foo': 'bar'}
        self.dumpTest([dup, dup],
            """
            ---
            - &1
                foo: bar
            - *1
            """)

    def testHashAlias(self):
        dup = {}
        dup['foo'] = dup
        self.dumpTest(dup, 
            """
            --- &1
            foo: *1
            """)

    def testEmptyArray(self):
        self.dumpTest([],
            """
            --- []
            """)

    def testEmptyHash(self):
        self.dumpTest({},
            """
            --- {}
            """)

    def testEmptyHashAsHashKey(self):
        self.dumpTest({'foo': {} },
            """
            ---
            foo: {}
            """)

    def testEmptyArrrayAsHashKey(self):
        self.dumpTest({'foo': [] },
            """
            ---
            foo: []
            """)

    def testAliasNone(self):
        foo = None
        self.dumpTest([foo, foo, foo],
            """
            ---
            - ~
            - ~
            - ~
            """)

    def testDumpMany(self):
        assertEquals(
            yaml.dump({'foo': 1}, ['a', 'b', 'c']),
            flushLeft(
                """
                ---
                foo: 1
                ---
                - a
                - b
                - c
                """))

    def testDumpExtraNewLines(self):
        self.dumpTest( "Joe's hot dogs.\n\n",
            """
            --- |+
            Joe's hot dogs.

            """)


    def testDumpExtraNewLinesInDict(self):
        data = {'preserving': "extra new lines are kept\n\n\n"}
        self.dumpTest(data,
            """
            ---
            preserving: |+
                extra new lines are kept
                
                
            """)

if __name__ == '__main__':
    import unittest
    unittest.main()
