# -*- coding: utf-8 -*-
"""Unit and functional test suite for cfget."""

from os import path
import sys
import imp
from cStringIO import StringIO

import unittest

if hasattr(sys, "dont_write_bytecode"):
    sys.dont_write_bytecode = True

cg = imp.load_source("cfget", "cfget")

class TestInterpreter(unittest.TestCase):
    """
    Test cfget expression interpreter
    """

    def setUp(self):
        """Method called by nose before running each test"""
        self.db = cg.Cfget()

        # Parse the input files
        self.db.load_file("ini", "testdata/general.ini")
        self.db.load_file("ini", "testdata/local.ini")

        # Load the dynamic sources
        self.db.load_plugin("testdata/dynamic.py")

    def tearDown(self):
        """Method called by nose after running each test"""
        pass

    def test_simple_expr(self):
        # Test the expression interpreter
        self.assertEquals(self.db.query("general/nothing"), None)
        self.assertEquals(self.db.query("general/answer"), "42")
        self.assertEquals(self.db.query("(general/answer)"), "42")
        self.assertEquals(self.db.query("general/answer + 1"), 43)
        self.assertEquals(self.db.query("general/answer * 2 + 1"), 85)
        self.assertEquals(self.db.query("general/answer / 2 + 1"), 22)
        self.assertEquals(self.db.query("general/answer * (2 + 1)"), 42*3)
        # Operator precedence
        self.assertEquals(self.db.query("general/answer + 1 * 2"), 44)
        # All operations are floating point
        self.assertEquals(self.db.query("general/answer / 4"), 10.5)
        # Functions
        self.assertEquals(self.db.query("int(general/answer / 4)"), 10)
        self.assertEquals(self.db.query("round(general/answer / 4)"), 11)
        self.assertEquals(self.db.query("round(3 * 5 / 2) + 2"), 10)

        # Curly braces in expressions
        self.assertEquals(self.db.query("master:{master/type}/num + 3"), 4)
        self.assertEquals(self.db.query("master:{master:{master/type}/val}/num + 3"), 5)

class TestCfget(unittest.TestCase):
    """
    Test basic cfget functionalities
    """

    def setUp(self):
        """Method called by nose before running each test"""
        self.db = cg.Cfget()

        # Parse the input files
        self.db.load_file("ini", "testdata/general.ini")
        self.db.load_file("ini", "testdata/local.ini")

        # Load the dynamic sources
        self.db.load_plugin("testdata/dynamic.py")


    def tearDown(self):
        """Method called by nose after running each test"""
        pass

    def test_static_keys(self):
        # Query nonexisting section
        self.assertEquals(self.db.query("missing/missing"), None)
        # Query nonexisting key in existing section
        self.assertEquals(self.db.query("general/missing"), None)
        # Query existing section/key
        self.assertEquals(self.db.query("general/name"), "Test run")
        # Querying is case insensitive
        self.assertEquals(self.db.query("General/name"), "Test run")
        self.assertEquals(self.db.query("general/Name"), "Test run")

        # Test regressions
        self.assertEquals(self.db.query("test_underscore/works"), "true")

        # Query in the second file
        self.assertEquals(self.db.query("general/instance"), "Local name")

    def test_dynamic_keys(self):
        # Query valid dynamic key
        self.assertEquals(self.db.query("general/duration"), "32 days, 1:01:01")
        # Ensure that looping keys don't loop
        self.assertEquals(self.db.query("general/loop"), None)

    def test_template(self):
        input = "@GENERAL_NAME@/@GENERAL_DURATION@"
        fd_out = StringIO()
        self.db.template("autoconf", StringIO(input), fd_out)
        self.assertEquals(fd_out.getvalue(), "Test run/32 days, 1:01:01")

    def test_custom_template(self):
        self.db.load_plugin("examples/templates.py")
        input = "_GENERAL_NAME_/_GENERAL_DURATION_"
        fd_out = StringIO()
        self.db.template("underscores", StringIO(input), fd_out)
        self.assertEquals(fd_out.getvalue(), "Test run/32 days, 1:01:01")

    def test_dump(self):
        fd_out = StringIO()
        self.db.dump("exports", fd_out, ["general/name", "general/duration"])
        self.assertEquals(fd_out.getvalue(),
                "export GENERAL_DURATION='32 days, 1:01:01'\n"
                "export GENERAL_NAME='Test run'\n")

        fd_out = StringIO()
        self.db.dump("repr", fd_out, ["general/name", "general/duration"])
        self.assertEquals(fd_out.getvalue(),
                "general/duration: '32 days, 1:01:01'\n"
                "general/name: 'Test run'\n")

    def test_custom_dump(self):
        self.db.load_plugin("examples/dumpers.py")
        fd_out = StringIO()
        self.db.dump("plain", fd_out, ["general/name", "general/duration"])
        self.assertEquals(fd_out.getvalue(),
                "general/duration: 32 days, 1:01:01\n"
                "general/name: Test run\n")

    def test_formats(self):
        self.db.load_file("configobj", "testdata/configobj.ini")
        self.assertEquals(self.db.query("configobj/home"), "/home/enrico")
        self.assertEquals(self.db.query("configobj/vimrc"), "/home/enrico/.vimrc")

    def test_custom_format(self):
        self.db.load_plugin("examples/formats.py")
        self.db.load_file("spaces", "testdata/spaces.cf")
        self.assertEquals(self.db.query("spaces"), "ok")
        self.assertEquals(self.db.query("general/name"), "foo")

    def test_curly(self):
        self.assertEquals(self.db.query("master:{master/type}/val"), "ok")
        self.assertEquals(self.db.query("master:{master:{master/type}/val}/val"), "yeah")

    #def test_funcs(self):
    #    self.assertEquals(self.db.query("query(master/type)"), "foo")

