"""Tests for various magic functions specific to the terminal frontend.

Needs to be run by nose (to make ipython session available).
"""
from __future__ import absolute_import

#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------

import sys
from StringIO import StringIO

import nose.tools as nt

from IPython.testing import tools as tt

#-----------------------------------------------------------------------------
# Test functions begin
#-----------------------------------------------------------------------------

def check_cpaste(code, should_fail=False):
    """Execute code via 'cpaste' and ensure it was executed, unless
    should_fail is set.
    """
    _ip.user_ns['code_ran'] = False

    src = StringIO()
    if not hasattr(src, 'encoding'):
        # IPython expects stdin to have an encoding attribute
        src.encoding = None
    src.write('\n')
    src.write(code)
    src.write('\n--\n')
    src.seek(0)

    stdin_save = sys.stdin
    sys.stdin = src

    try:
        context = tt.AssertPrints if should_fail else tt.AssertNotPrints
        with context("Traceback (most recent call last)"):
                _ip.magic('cpaste')

        if not should_fail:
            assert _ip.user_ns['code_ran']
    finally:
        sys.stdin = stdin_save

PY31 = sys.version_info[:2] == (3,1)

def test_cpaste():
    """Test cpaste magic"""

    def run():
        """Marker function: sets a flag when executed.
        """
        _ip.user_ns['code_ran'] = True
        return 'run' # return string so '+ run()' doesn't result in success

    tests = {'pass': ["run()",
                      "In [1]: run()",
                      "In [1]: if 1:\n   ...:     run()",
                      "> > > run()",
                      ">>> run()",
                      "   >>> run()",
                      ],

             'fail': ["1 + run()",
             ]}
    
    # I don't know why this is failing specifically on Python 3.1. I've
    # checked it manually interactively, but we don't care enough about 3.1
    # to spend time fiddling with the tests, so we just skip it.
    if not PY31:
        tests['fail'].append("++ run()")

    _ip.user_ns['run'] = run

    for code in tests['pass']:
        check_cpaste(code)

    for code in tests['fail']:
        check_cpaste(code, should_fail=True)


# Multiple tests for clipboard pasting
def test_paste():
    _ip = get_ipython()

    def paste(txt, flags='-q'):
        """Paste input text, by default in quiet mode"""
        hooks.clipboard_get = lambda : txt
        _ip.magic('paste '+flags)

    # Inject fake clipboard hook but save original so we can restore it later
    hooks = _ip.hooks
    user_ns = _ip.user_ns
    original_clip = hooks.clipboard_get

    try:
        # Run tests with fake clipboard function
        user_ns.pop('x', None)
        paste('x=1')
        nt.assert_equal(user_ns['x'], 1)

        user_ns.pop('x', None)
        paste('>>> x=2')
        nt.assert_equal(user_ns['x'], 2)

        paste("""
        >>> x = [1,2,3]
        >>> y = []
        >>> for i in x:
        ...     y.append(i**2)
        ...
        """)
        nt.assert_equal(user_ns['x'], [1,2,3])
        nt.assert_equal(user_ns['y'], [1,4,9])

        # Now, test that paste -r works
        user_ns.pop('x', None)
        nt.assert_false('x' in user_ns)
        _ip.magic('paste -r')
        nt.assert_equal(user_ns['x'], [1,2,3])

        # Test pasting of email-quoted contents
        paste("""
        >> def foo(x):
        >>     return x + 1
        >> x = foo(1.1)
        """)
        nt.assert_equal(user_ns['x'], 2.1)

        # Email again; some programs add a space also at each quoting level
        paste("""
        > > def foo(x):
        > >     return x + 1
        > > x = foo(2.1)
        """)
        nt.assert_equal(user_ns['x'], 3.1)

        # Email quoting of interactive input
        paste("""
        >> >>> def f(x):
        >> ...   return x+1
        >> ...
        >> >>> x = f(2.5)
        """)
        nt.assert_equal(user_ns['x'], 3.5)

        # Also test paste echoing, by temporarily faking the writer
        w = StringIO()
        writer = _ip.write
        _ip.write = w.write
        code = """
        a = 100
        b = 200"""
        try:
            paste(code,'')
            out = w.getvalue()
        finally:
            _ip.write = writer
        nt.assert_equal(user_ns['a'], 100)
        nt.assert_equal(user_ns['b'], 200)
        nt.assert_equal(out, code+"\n## -- End pasted text --\n")

    finally:
        # Restore original hook
        hooks.clipboard_get = original_clip
