#!/usr/bin/env python

import site, os, sys
site.addsitedir(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
    
__requires__='Bazki'
import pkg_resources
pkg_resources.require('Bazki')

import unittest
from cStringIO import StringIO

from bazyaml import format

class BaseHandler(object):
    def prop(self, e, p, contents):
        return contents
    def start_element(self, e, parent=None):
        return True
    def end_element(self, e):
        pass
    def get_new_properties(self, e):
        return {}
    def get_new_children(self, e):
        return []
    def set_extern(self, name, val):
        assert False

class FormatTest(unittest.TestCase):

    def expect_becomes(self, initial, handler, expected):
        inf = StringIO(initial)
        outf = StringIO()
        format.parse('/tmp/test.yaml', handler, inf, outf)
        self.assertEquals(expected, outf.getvalue())

    def expect_fails(self, initial, handler, message):
        inf = StringIO(initial)
        outf = StringIO()
        try:
            format.parse('/tmp/test.yaml', handler, inf, outf)
        except format.InvalidBazFile, e:
            self.assertTrue(message in e.args[0], e.args[0])
        else:
            self.fail("Parsing succeeded.")

    def test_consistency(self):
        handler = BaseHandler()

        for y in ("""foo: bar
baz: 3
quux: True
boingo: 'a sound effect'
""",
                  """foo: bar
baz: |
  This is
  a block
  with returns.
""",
                  """foo: bar
baz: |
  This is 
  a block 
  with end-of-line spaces.
""",
                  
                  ):
            self.expect_becomes(y, handler, y)

    def test_flow(self):
        handler = BaseHandler()

        y = """foo: bar
baz: >
  This is
  a block
  that flows.
"""
        z = """foo: bar
baz: >
  This is a block that flows.
"""
        self.expect_becomes(y, handler, z)

    def test_prop_setting(self):
        class SettingHandler(BaseHandler):
            def prop(self, e, p, contents):
                return contents + "!"
        handler = SettingHandler()

        y = """foo: bar
baz: 3
quux: True
boingo: a sound effect
"""
        z = """foo: bar!
baz: 3!
quux: True!
boingo: a sound effect!
"""
        self.expect_becomes(y, handler, z)

        y = """foo: bar
baz: |
  This is
  a block
  with returns.
"""
        z = """foo: bar!
baz: |-
  This is
  a block
  with returns.
  !
"""
        self.expect_becomes(y, handler, z)

        y = """foo: bar
baz: >
  This is
  a block
  that flows.
"""
        z = """foo: bar!
baz: |-
  This is a block that flows.
  !
"""
        self.expect_becomes(y, handler, z)

    def test_new_prop(self):
        class NewPropHandler(BaseHandler):
            def get_new_properties(self, e):
                return {u'boingo': u'Cheese.'}
        handler = NewPropHandler()

        y = """foo: bar
"""
        z = """foo: bar
boingo: Cheese.
"""

        self.expect_becomes(y, handler, z)

        z = "boingo: Cheese.\n"
        self.expect_becomes("", handler, z)
        self.expect_becomes("\n", handler, z)

        z = "---\nboingo: Cheese.\n"
        self.expect_becomes("---", handler, z)
        self.expect_becomes("---\n", handler, z)

    def test_new_multiline_props(self):
        for a, b in [(u'Cheese.\n\nOr not to cheese.\n',
                      "|\n  Cheese.\n\n  Or not to cheese.\n"),
                     (u'Cheese.\nOr not to cheese.\n',
                      "|\n  Cheese.\n  Or not to cheese.\n"),
                     (u'Cheese.\nOr not to cheese.',
                      "|-\n  Cheese.\n  Or not to cheese.\n"),
                     (u'Cheese. \nOr not to cheese.',
                      "|-\n  Cheese. \n  Or not to cheese.\n"),
                     (u'Amnesiac with no aspects, possibly newly created clone or Dollhouse\nperson.',
                      "|-\n  Amnesiac with no aspects, possibly newly created clone or Dollhouse\n  person.\n"),
                     (u'Amnesiac with no aspects, possibly newly created clone or Dollhouse \nperson.',
                      "|-\n  Amnesiac with no aspects, possibly newly created clone or Dollhouse \n  person.\n"),
                     ]:
            class NewPropHandler(BaseHandler):
                def get_new_properties(self, e):
                    return {u'boingo': a}
            handler = NewPropHandler()

            z = "boingo: " + b
            self.expect_becomes("", handler, z)
            self.expect_becomes("\n", handler, z)

            z = "---\nboingo: " + b
            self.expect_becomes("---", handler, z)
            self.expect_becomes("---\n", handler, z)

            y = "foo: bar\n"
            z = "foo: bar\nboingo: " + b
            self.expect_becomes(y, handler, z)

    def test_include_consistency(self):
        class IncludeHandler(BaseHandler):
            def get_include(self, path):
                return "Wombat!"
        handler = IncludeHandler()

        y = """foo: bar
boingo: !include 'attach.txt'
"""

        self.expect_becomes(y, handler, y)

    def test_include_setting(self):
        class IncludeSettingHandler(BaseHandler):
            def get_include(self, path):
                return ("Wombat!")
            def prop(self, e, p, contents):
                if p == 'boingo':
                    return format.Include(contents.val + "!", '.txt')
                else:
                    return contents
            def set_include(self, path, val, new):
                assert val == ("Wombat!!"), str(val)
                assert not new
                return path
        handler = IncludeSettingHandler()

        y = """foo: bar
boingo: !include 'attach.txt'
"""

        self.expect_becomes(y, handler, y)
        

    def test_include_to_inline(self):
        class IncludeSettingHandler(BaseHandler):
            def get_include(self, path):
                return ("Wombat!")
            def prop(self, e, p, contents):
                if p == 'boingo':
                    return unicode(contents.val) + "!"
                else:
                    return contents
        handler = IncludeSettingHandler()

        y = """foo: bar
boingo: !include attach.txt
"""
        z = """foo: bar
boingo: Wombat!!
"""

        self.expect_becomes(y, handler, z)

    def test_inline_to_include(self):
        class IncludeSettingHandler(BaseHandler):
            def prop(self, e, p, contents):
                if p == 'boingo':
                    return format.Include(contents + "!", '.txt')
                else:
                    return contents
            def set_include(self, path, val, new):
                assert val == ("Wombat!!"), str(val)
                assert new
                return path
        handler = IncludeSettingHandler()

        y = """foo: bar
boingo: Wombat!
"""
        z = """foo: bar
boingo: !include 'test.boingo.txt'
"""

        self.expect_becomes(y, handler, z)

    def test_new_include(self):
        class NewIncludeHandler(BaseHandler):
            def set_include(self, path, val, new):
                assert path == 'test.boingo.txt', path
                assert val == ("Cheese."), str(val)
                assert new
                return 'upload.txt'
            def get_new_properties(self, e):
                return {u'boingo': ('.txt', 'Cheese.')}
        handler = NewIncludeHandler()

        y = """foo: bar
"""
        z = """foo: bar
boingo: !include 'upload.txt'
"""

        self.expect_becomes(y, handler, z)
    
    def test_empty(self):
        self.expect_becomes("", BaseHandler(), "")
        self.expect_becomes("\n", BaseHandler(), "")

    def test_dashes(self):
        self.expect_becomes("---", BaseHandler(), "---\n")
        self.expect_becomes("---\n", BaseHandler(), "---\n")

    def test_mapping_with_dashes(self):
        handler = BaseHandler()

        y = """---
foo: bar
baz: 3
quux: True
boingo: 'a sound effect'
"""
        self.expect_becomes(y, handler, y)

    def test_equals_signs(self):
        self.expect_fails("===\nfoo: bar\n", BaseHandler(),
                          "Non-empty scalar '=== foo' at start of mapping.")

    def test_square_brackets(self):
        self.expect_fails("foo: [[bar]]\n", BaseHandler(),
                          "values starting with a [ must be quoted")

    def test_curly_brackets(self):
        self.expect_fails("foo: {{bar}}\n", BaseHandler(),
                          "values starting with a { must be quoted")

    def test_nonlocal_include(self):
        self.expect_fails("foo: !include bar/baz.png\n", BaseHandler(),
                          "Includes must not be in a different directory")

    def test_yaml_include(self):
        self.expect_fails("foo: !include bar.yaml\n", BaseHandler(),
                          "Includes must not end in .yaml")

    def test_asterisk(self):
        self.expect_fails("foo: **bar**\n", BaseHandler(),
                          "values starting with a * must be quoted")

    def test_question_mark(self):
        self.expect_fails("foo: ? bar\n", BaseHandler(),
                          "values starting with a ? must be quoted")

    def test_question_mark_alone(self):
        self.expect_fails("foo: ?\n", BaseHandler(),
                          "values starting with a ? must be quoted")

    def test_duplicate_propval_fails(self):
        self.expect_fails("foo: bar\nfoo: baz\n", BaseHandler(),
                          "set more than once")

if __name__ == "__main__":
    import warnings
    warnings.simplefilter("error")
    unittest.main()
