# coding=utf-8
"""
Cmd2 unit/functional testing

Copyright 2016 Federico Ceratto <federico.ceratto@gmail.com>
Released under MIT license, see LICENSE file
"""
import argparse
import builtins
from code import InteractiveConsole
import io
import os
import sys
import tempfile

import pytest

# Python 3.5 had some regressions in the unitest.mock module, so use 3rd party mock if available
try:
    import mock
except ImportError:
    from unittest import mock

import cmd2
from cmd2 import clipboard
from cmd2 import utils
from .conftest import run_cmd, normalize, BASE_HELP, BASE_HELP_VERBOSE, \
    HELP_HISTORY, SHORTCUTS_TXT, SHOW_TXT, SHOW_LONG, StdOut


def test_ver():
    assert cmd2.__version__ == '0.9.3'


def test_empty_statement(base_app):
    out = run_cmd(base_app, '')
    expected = normalize('')
    assert out == expected

def test_base_help(base_app):
    out = run_cmd(base_app, 'help')
    expected = normalize(BASE_HELP)
    assert out == expected

def test_base_help_verbose(base_app):
    out = run_cmd(base_app, 'help -v')
    expected = normalize(BASE_HELP_VERBOSE)
    assert out == expected

    out = run_cmd(base_app, 'help --verbose')
    assert out == expected

def test_base_help_history(base_app):
    out = run_cmd(base_app, 'help history')
    assert out == normalize(HELP_HISTORY)

def test_base_argparse_help(base_app, capsys):
    # Verify that "set -h" gives the same output as "help set" and that it starts in a way that makes sense
    run_cmd(base_app, 'set -h')
    out, err = capsys.readouterr()
    out1 = normalize(str(out))

    out2 = run_cmd(base_app, 'help set')

    assert out1 == out2
    assert out1[0].startswith('usage: set')
    assert out1[1] == ''
    assert out1[2].startswith('Sets a settable parameter')

def test_base_invalid_option(base_app, capsys):
    run_cmd(base_app, 'set -z')
    out, err = capsys.readouterr()
    out = normalize(out)
    err = normalize(err)
    assert len(err) == 3
    assert len(out) == 15
    assert 'Error: unrecognized arguments: -z' in err[0]
    assert out[0] == 'usage: set [-h] [-a] [-l] [settable [settable ...]]'

def test_base_shortcuts(base_app):
    out = run_cmd(base_app, 'shortcuts')
    expected = normalize(SHORTCUTS_TXT)
    assert out == expected


def test_base_show(base_app):
    # force editor to be 'vim' so test is repeatable across platforms
    base_app.editor = 'vim'
    out = run_cmd(base_app, 'set')
    expected = normalize(SHOW_TXT)
    assert out == expected


def test_base_show_long(base_app):
    # force editor to be 'vim' so test is repeatable across platforms
    base_app.editor = 'vim'
    out = run_cmd(base_app, 'set -l')
    expected = normalize(SHOW_LONG)
    assert out == expected


def test_base_show_readonly(base_app):
    base_app.editor = 'vim'
    out = run_cmd(base_app, 'set -a')
    expected = normalize(SHOW_TXT + '\nRead only settings:' + """
        Commands may be terminated with: {}
        Arguments at invocation allowed: {}
        Output redirection and pipes allowed: {}
""".format(base_app.terminators, base_app.allow_cli_args, base_app.allow_redirection))
    assert out == expected


def test_cast():
    # Boolean
    assert utils.cast(True, True) == True
    assert utils.cast(True, False) == False
    assert utils.cast(True, 0) == False
    assert utils.cast(True, 1) == True
    assert utils.cast(True, 'on') == True
    assert utils.cast(True, 'off') == False
    assert utils.cast(True, 'ON') == True
    assert utils.cast(True, 'OFF') == False
    assert utils.cast(True, 'y') == True
    assert utils.cast(True, 'n') == False
    assert utils.cast(True, 't') == True
    assert utils.cast(True, 'f') == False

    # Non-boolean same type
    assert utils.cast(1, 5) == 5
    assert utils.cast(3.4, 2.7) == 2.7
    assert utils.cast('foo', 'bar') == 'bar'
    assert utils.cast([1,2], [3,4]) == [3,4]

def test_cast_problems(capsys):
    expected = 'Problem setting parameter (now {}) to {}; incorrect type?\n'

    # Boolean current, with new value not convertible to bool
    current = True
    new = [True, True]
    assert utils.cast(current, new) == current
    out, err = capsys.readouterr()
    assert out == expected.format(current, new)

    # Non-boolean current, with new value not convertible to current type
    current = 1
    new = 'octopus'
    assert utils.cast(current, new) == current
    out, err = capsys.readouterr()
    assert out == expected.format(current, new)


def test_base_set(base_app):
    out = run_cmd(base_app, 'set quiet True')
    expected = normalize("""
quiet - was: False
now: True
""")
    assert out == expected

    out = run_cmd(base_app, 'set quiet')
    assert out == ['quiet: True']

def test_set_not_supported(base_app, capsys):
    run_cmd(base_app, 'set qqq True')
    out, err = capsys.readouterr()
    expected = normalize("""
EXCEPTION of type 'LookupError' occurred with message: 'Parameter 'qqq' not supported (type 'set' for list of parameters).'
To enable full traceback, run the following command:  'set debug true'
""")
    assert normalize(str(err)) == expected

def test_set_quiet(base_app):
    out = run_cmd(base_app, 'set quie True')
    expected = normalize("""
quiet - was: False
now: True
""")
    assert out == expected

    out = run_cmd(base_app, 'set quiet')
    assert out == ['quiet: True']


def test_base_shell(base_app, monkeypatch):
    m = mock.Mock()
    monkeypatch.setattr("{}.Popen".format('subprocess'), m)
    out = run_cmd(base_app, 'shell echo a')
    assert out == []
    assert m.called

def test_base_py(base_app, capsys):
    run_cmd(base_app, 'py qqq=3')
    out, err = capsys.readouterr()
    assert out == ''
    run_cmd(base_app, 'py print(qqq)')
    out, err = capsys.readouterr()
    assert out.rstrip() == '3'


@pytest.mark.skipif(sys.platform == 'win32',
                    reason="Unit test doesn't work on win32, but feature does")
def test_base_run_python_script(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    python_script = os.path.join(test_dir, 'script.py')
    expected = 'This is a python script running ...\n'

    run_cmd(base_app, "py run('{}')".format(python_script))
    out, err = capsys.readouterr()
    assert out == expected


def test_base_run_pyscript(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    python_script = os.path.join(test_dir, 'script.py')
    expected = 'This is a python script running ...\n'

    run_cmd(base_app, "pyscript {}".format(python_script))
    out, err = capsys.readouterr()
    assert out == expected

def test_recursive_pyscript_not_allowed(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    python_script = os.path.join(test_dir, 'scripts', 'recursive.py')
    expected = 'ERROR: Recursively entering interactive Python consoles is not allowed.\n'

    run_cmd(base_app, "pyscript {}".format(python_script))
    out, err = capsys.readouterr()
    assert err == expected

def test_pyscript_with_nonexist_file(base_app, capsys):
    python_script = 'does_not_exist.py'
    run_cmd(base_app, "pyscript {}".format(python_script))
    out, err = capsys.readouterr()
    assert err.startswith("EXCEPTION of type 'FileNotFoundError' occurred with message:")

def test_pyscript_with_exception(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    python_script = os.path.join(test_dir, 'scripts', 'raises_exception.py')
    run_cmd(base_app, "pyscript {}".format(python_script))
    out, err = capsys.readouterr()
    assert err.startswith('Traceback')
    assert err.endswith("TypeError: unsupported operand type(s) for +: 'int' and 'str'\n")

def test_pyscript_requires_an_argument(base_app, capsys):
    run_cmd(base_app, "pyscript")
    out, err = capsys.readouterr()
    assert err.startswith('ERROR: pyscript command requires at least 1 argument ...')


def test_base_error(base_app):
    out = run_cmd(base_app, 'meow')
    assert out == ["*** Unknown syntax: meow"]


@pytest.fixture
def hist():
    from cmd2.cmd2 import History, HistoryItem
    h = History([HistoryItem('first'), HistoryItem('second'), HistoryItem('third'), HistoryItem('fourth')])
    return h

def test_history_span(hist):
    h = hist
    assert h == ['first', 'second', 'third', 'fourth']
    assert h.span('-2..') == ['third', 'fourth']
    assert h.span('2..3') == ['second', 'third']    # Inclusive of end
    assert h.span('3') == ['third']
    assert h.span(':') == h
    assert h.span('2..') == ['second', 'third', 'fourth']
    assert h.span('-1') == ['fourth']
    assert h.span('-2..-3') == ['third', 'second']
    assert h.span('*') == h

def test_history_get(hist):
    h = hist
    assert h == ['first', 'second', 'third', 'fourth']
    assert h.get('') == h
    assert h.get('-2') == h[:-2]
    assert h.get('5') == []
    assert h.get('2-3') == ['second']           # Exclusive of end
    assert h.get('ir') == ['first', 'third']    # Normal string search for all elements containing "ir"
    assert h.get('/i.*d/') == ['third']         # Regex string search "i", then anything, then "d"

def test_base_history(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    out = run_cmd(base_app, 'history')
    expected = normalize("""
-------------------------[1]
help
-------------------------[2]
shortcuts
""")
    assert out == expected

    out = run_cmd(base_app, 'history he')
    expected = normalize("""
-------------------------[1]
help
""")
    assert out == expected

    out = run_cmd(base_app, 'history sh')
    expected = normalize("""
-------------------------[2]
shortcuts
""")
    assert out == expected

def test_history_script_format(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    out = run_cmd(base_app, 'history -s')
    expected = normalize("""
help
shortcuts
""")
    assert out == expected

def test_history_with_string_argument(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    run_cmd(base_app, 'help history')
    out = run_cmd(base_app, 'history help')
    expected = normalize("""
-------------------------[1]
help
-------------------------[3]
help history
""")
    assert out == expected


def test_history_with_integer_argument(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    out = run_cmd(base_app, 'history 1')
    expected = normalize("""
-------------------------[1]
help
""")
    assert out == expected


def test_history_with_integer_span(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    run_cmd(base_app, 'help history')
    out = run_cmd(base_app, 'history 1..2')
    expected = normalize("""
-------------------------[1]
help
-------------------------[2]
shortcuts
""")
    assert out == expected

def test_history_with_span_start(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    run_cmd(base_app, 'help history')
    out = run_cmd(base_app, 'history 2:')
    expected = normalize("""
-------------------------[2]
shortcuts
-------------------------[3]
help history
""")
    assert out == expected

def test_history_with_span_end(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    run_cmd(base_app, 'help history')
    out = run_cmd(base_app, 'history :2')
    expected = normalize("""
-------------------------[1]
help
-------------------------[2]
shortcuts
""")
    assert out == expected

def test_history_with_span_index_error(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'help history')
    run_cmd(base_app, '!ls -hal :')
    out = run_cmd(base_app, 'history "hal :"')
    expected = normalize("""
-------------------------[3]
!ls -hal :
""")
    assert out == expected

def test_history_output_file(base_app):
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'shortcuts')
    run_cmd(base_app, 'help history')

    fd, fname = tempfile.mkstemp(prefix='', suffix='.txt')
    os.close(fd)
    run_cmd(base_app, 'history -o "{}"'.format(fname))
    expected = normalize('\n'.join(['help', 'shortcuts', 'help history']))
    with open(fname) as f:
        content = normalize(f.read())
    assert content == expected

def test_history_edit(base_app, monkeypatch):
    # Set a fake editor just to make sure we have one.  We aren't really
    # going to call it due to the mock
    base_app.editor = 'fooedit'

    # Mock out the os.system call so we don't actually open an editor
    m = mock.MagicMock(name='system')
    monkeypatch.setattr("os.system", m)

    # Run help command just so we have a command in history
    run_cmd(base_app, 'help')
    run_cmd(base_app, 'history -e 1')

    # We have an editor, so should expect a system call
    m.assert_called_once()

def test_history_run_all_commands(base_app):
    # make sure we refuse to run all commands as a default
    run_cmd(base_app, 'shortcuts')
    out = run_cmd(base_app, 'history -r')
    # this should generate an error, but we don't currently have a way to
    # capture stderr in these tests. So we assume that if we got nothing on
    # standard out, that the error occured because if the commaned executed
    # then we should have a list of shortcuts in our output
    assert out == []

def test_history_run_one_command(base_app):
    expected = run_cmd(base_app, 'help')
    output = run_cmd(base_app, 'history -r 1')
    assert output == expected

def test_base_load(base_app, request):
    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'script.txt')

    assert base_app.cmdqueue == []
    assert base_app._script_dir == []
    assert base_app._current_script_dir is None

    # Run the load command, which populates the command queue and sets the script directory
    run_cmd(base_app, 'load {}'.format(filename))

    assert base_app.cmdqueue == ['help history', 'eos']
    sdir = os.path.dirname(filename)
    assert base_app._script_dir == [sdir]
    assert base_app._current_script_dir == sdir

def test_load_with_empty_args(base_app, capsys):
    # The way the load command works, we can't directly capture its stdout or stderr
    run_cmd(base_app, 'load')
    out, err = capsys.readouterr()

    # The load command requires a file path argument, so we should get an error message
    expected = normalize("""ERROR: load command requires a file path:\n""")
    assert normalize(str(err)) == expected
    assert base_app.cmdqueue == []


def test_load_with_nonexistent_file(base_app, capsys):
    # The way the load command works, we can't directly capture its stdout or stderr
    run_cmd(base_app, 'load does_not_exist.txt')
    out, err = capsys.readouterr()

    # The load command requires a path to an existing file
    assert str(err).startswith("ERROR")
    assert "does not exist or is not a file" in str(err)
    assert base_app.cmdqueue == []


def test_load_with_empty_file(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'scripts', 'empty.txt')

    # The way the load command works, we can't directly capture its stdout or stderr
    run_cmd(base_app, 'load {}'.format(filename))
    out, err = capsys.readouterr()

    # The load command requires non-empty scripts files
    assert str(err).startswith("ERROR")
    assert "is empty" in str(err)
    assert base_app.cmdqueue == []


def test_load_with_binary_file(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'scripts', 'binary.bin')

    # The way the load command works, we can't directly capture its stdout or stderr
    run_cmd(base_app, 'load {}'.format(filename))
    out, err = capsys.readouterr()

    # The load command requires non-empty scripts files
    assert str(err).startswith("ERROR")
    assert "is not an ASCII or UTF-8 encoded text file" in str(err)
    assert base_app.cmdqueue == []


def test_load_with_utf8_file(base_app, capsys, request):
    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'scripts', 'utf8.txt')

    assert base_app.cmdqueue == []
    assert base_app._script_dir == []
    assert base_app._current_script_dir is None

    # Run the load command, which populates the command queue and sets the script directory
    run_cmd(base_app, 'load {}'.format(filename))

    assert base_app.cmdqueue == ['!echo γνωρίζω', 'eos']
    sdir = os.path.dirname(filename)
    assert base_app._script_dir == [sdir]
    assert base_app._current_script_dir == sdir


def test_load_nested_loads(base_app, request):
    # Verify that loading a script with nested load commands works correctly,
    # and loads the nested script commands in the correct order. The recursive
    # loads don't happen all at once, but as the commands are interpreted. So,
    # we will need to drain the cmdqueue and inspect the stdout to see if all
    # steps were executed in the expected order.
    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'scripts', 'nested.txt')
    assert base_app.cmdqueue == []

    # Load the top level script and then run the command queue until all
    # commands have been exhausted.
    initial_load = 'load ' + filename
    run_cmd(base_app, initial_load)
    while base_app.cmdqueue:
        base_app.onecmd_plus_hooks(base_app.cmdqueue.pop(0))

    # Check that the right commands were executed.
    expected = """
%s
_relative_load precmds.txt
set colors on
help
shortcuts
_relative_load postcmds.txt
set colors off""" % initial_load
    assert run_cmd(base_app, 'history -s') == normalize(expected)


def test_base_runcmds_plus_hooks(base_app, request):
    # Make sure that runcmds_plus_hooks works as intended. I.E. to run multiple
    # commands and process any commands added, by them, to the command queue.
    test_dir = os.path.dirname(request.module.__file__)
    prefilepath = os.path.join(test_dir, 'scripts', 'precmds.txt')
    postfilepath = os.path.join(test_dir, 'scripts', 'postcmds.txt')
    assert base_app.cmdqueue == []

    base_app.runcmds_plus_hooks(['load ' + prefilepath,
                                 'help',
                                 'shortcuts',
                                 'load ' + postfilepath])
    expected = """
load %s
set colors on
help
shortcuts
load %s
set colors off""" % (prefilepath, postfilepath)
    assert run_cmd(base_app, 'history -s') == normalize(expected)


def test_base_relative_load(base_app, request):
    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'script.txt')

    assert base_app.cmdqueue == []
    assert base_app._script_dir == []
    assert base_app._current_script_dir is None

    # Run the load command, which populates the command queue and sets the script directory
    run_cmd(base_app, '_relative_load {}'.format(filename))

    assert base_app.cmdqueue == ['help history', 'eos']
    sdir = os.path.dirname(filename)
    assert base_app._script_dir == [sdir]
    assert base_app._current_script_dir == sdir

def test_relative_load_requires_an_argument(base_app, capsys):
    run_cmd(base_app, '_relative_load')
    out, err = capsys.readouterr()
    assert out == ''
    assert err.startswith('ERROR: _relative_load command requires a file path:\n')
    assert base_app.cmdqueue == []


def test_output_redirection(base_app):
    fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt')
    os.close(fd)

    try:
        # Verify that writing to a file works
        run_cmd(base_app, 'help > {}'.format(filename))
        expected = normalize(BASE_HELP)
        with open(filename) as f:
            content = normalize(f.read())
        assert content == expected

        # Verify that appending to a file also works
        run_cmd(base_app, 'help history >> {}'.format(filename))
        expected = normalize(BASE_HELP + '\n' + HELP_HISTORY)
        with open(filename) as f:
            content = normalize(f.read())
        assert content == expected
    except:
        raise
    finally:
        os.remove(filename)

def test_output_redirection_to_nonexistent_directory(base_app):
    filename = '~/fakedir/this_does_not_exist.txt'

    # Verify that writing to a file in a non-existent directory doesn't work
    run_cmd(base_app, 'help > {}'.format(filename))
    expected = normalize(BASE_HELP)
    with pytest.raises(FileNotFoundError):
        with open(filename) as f:
            content = normalize(f.read())
        assert content == expected

    # Verify that appending to a file also works
    run_cmd(base_app, 'help history >> {}'.format(filename))
    expected = normalize(BASE_HELP + '\n' + HELP_HISTORY)
    with pytest.raises(FileNotFoundError):
        with open(filename) as f:
            content = normalize(f.read())
        assert content == expected

def test_output_redirection_to_too_long_filename(base_app):
    filename = '~/sdkfhksdjfhkjdshfkjsdhfkjsdhfkjdshfkjdshfkjshdfkhdsfkjhewfuihewiufhweiufhiweufhiuewhiuewhfiuwehfiuewhfiuewhfiuewhfiuewhiuewhfiuewhfiuewfhiuwehewiufhewiuhfiweuhfiuwehfiuewfhiuwehiuewfhiuewhiewuhfiuewhfiuwefhewiuhewiufhewiufhewiufhewiufhewiufhewiufhewiufhewiuhewiufhewiufhewiuheiufhiuewheiwufhewiufheiufheiufhieuwhfewiuhfeiufhiuewfhiuewheiwuhfiuewhfiuewhfeiuwfhewiufhiuewhiuewhfeiuwhfiuwehfuiwehfiuehiuewhfieuwfhieufhiuewhfeiuwfhiuefhueiwhfw'

    # Verify that writing to a file in a non-existent directory doesn't work
    run_cmd(base_app, 'help > {}'.format(filename))
    expected = normalize(BASE_HELP)
    with pytest.raises(OSError):
        with open(filename) as f:
            content = normalize(f.read())
        assert content == expected

    # Verify that appending to a file also works
    run_cmd(base_app, 'help history >> {}'.format(filename))
    expected = normalize(BASE_HELP + '\n' + HELP_HISTORY)
    with pytest.raises(OSError):
        with open(filename) as f:
            content = normalize(f.read())
        assert content == expected


def test_feedback_to_output_true(base_app):
    base_app.feedback_to_output = True
    base_app.timing = True
    f, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt')
    os.close(f)

    try:
        run_cmd(base_app, 'help > {}'.format(filename))
        with open(filename) as f:
            content = f.readlines()
        assert content[-1].startswith('Elapsed: ')
    except:
        raise
    finally:
        os.remove(filename)


def test_feedback_to_output_false(base_app, capsys):
    base_app.feedback_to_output = False
    base_app.timing = True
    f, filename = tempfile.mkstemp(prefix='feedback_to_output', suffix='.txt')
    os.close(f)

    try:
        run_cmd(base_app, 'help > {}'.format(filename))
        out, err = capsys.readouterr()
        with open(filename) as f:
            content = f.readlines()
        assert not content[-1].startswith('Elapsed: ')
        assert err.startswith('Elapsed')
    except:
        raise
    finally:
        os.remove(filename)


def test_allow_redirection(base_app):
    # Set allow_redirection to False
    base_app.allow_redirection = False

    filename = 'test_allow_redirect.txt'

    # Verify output wasn't redirected
    out = run_cmd(base_app, 'help > {}'.format(filename))
    expected = normalize(BASE_HELP)
    assert out == expected

    # Verify that no file got created
    assert not os.path.exists(filename)

def test_pipe_to_shell(base_app, capsys):
    if sys.platform == "win32":
        # Windows
        command = 'help | sort'
    else:
        # Mac and Linux
        # Get help on help and pipe it's output to the input of the word count shell command
        command = 'help help | wc'
        # # Mac and Linux wc behave the same when piped from shell, but differently when piped stdin from file directly
        # if sys.platform == 'darwin':
        #     expected = "1      11      70"
        # else:
        #     expected = "1 11 70"
        # assert out.strip() == expected.strip()

    run_cmd(base_app, command)
    out, err = capsys.readouterr()

    # Unfortunately with the improved way of piping output to a subprocess, there isn't any good way of getting
    # access to the output produced by that subprocess within a unit test, but we can verify that no error occured
    assert not err

def test_pipe_to_shell_error(base_app, capsys):
    # Try to pipe command output to a shell command that doesn't exist in order to produce an error
    run_cmd(base_app, 'help | foobarbaz.this_does_not_exist')
    out, err = capsys.readouterr()
    assert not out
    expected_error = 'FileNotFoundError'
    assert err.startswith("EXCEPTION of type '{}' occurred with message:".format(expected_error))


@pytest.mark.skipif(not clipboard.can_clip,
                    reason="Pyperclip could not find a copy/paste mechanism for your system")
def test_send_to_paste_buffer(base_app):
    # Test writing to the PasteBuffer/Clipboard
    run_cmd(base_app, 'help >')
    expected = normalize(BASE_HELP)
    assert normalize(cmd2.cmd2.get_paste_buffer()) == expected

    # Test appending to the PasteBuffer/Clipboard
    run_cmd(base_app, 'help history >>')
    expected = normalize(BASE_HELP + '\n' + HELP_HISTORY)
    assert normalize(cmd2.cmd2.get_paste_buffer()) == expected


def test_base_timing(base_app, capsys):
    base_app.feedback_to_output = False
    out = run_cmd(base_app, 'set timing True')
    expected = normalize("""timing - was: False
now: True
""")
    assert out == expected
    out, err = capsys.readouterr()
    if sys.platform == 'win32':
        assert err.startswith('Elapsed: 0:00:00')
    else:
        assert err.startswith('Elapsed: 0:00:00.0')


def test_base_debug(base_app, capsys):
    # Try to set a non-existent parameter with debug set to False by default
    run_cmd(base_app, 'set does_not_exist 5')
    out, err = capsys.readouterr()
    assert err.startswith('EXCEPTION')

    # Set debug true
    out = run_cmd(base_app, 'set debug True')
    expected = normalize("""
debug - was: False
now: True
""")
    assert out == expected

    # Verify that we now see the exception traceback
    run_cmd(base_app, 'set does_not_exist 5')
    out, err = capsys.readouterr()
    assert str(err).startswith('Traceback (most recent call last):')


def test_base_colorize(base_app):
    # If using base_app test fixture it won't get colorized because we replaced self.stdout
    color_test = base_app.colorize('Test', 'red')
    assert color_test == 'Test'

    # But if we create a fresh Cmd() instance, it will
    fresh_app = cmd2.Cmd()
    color_test = fresh_app.colorize('Test', 'red')
    # Actually, colorization only ANSI escape codes is only applied on non-Windows systems
    if sys.platform == 'win32':
        assert color_test == 'Test'
    else:
        assert color_test == '\x1b[31mTest\x1b[39m'


def _expected_no_editor_error():
    expected_exception = 'OSError'
    # If PyPy, expect a different exception than with Python 3
    if hasattr(sys, "pypy_translation_info"):
        expected_exception = 'EnvironmentError'

    expected_text = normalize("""
EXCEPTION of type '{}' occurred with message: 'Please use 'set editor' to specify your text editing program of choice.'
To enable full traceback, run the following command:  'set debug true'
""".format(expected_exception))

    return expected_text

def test_edit_no_editor(base_app, capsys):
    # Purposely set the editor to None
    base_app.editor = None

    # Make sure we get an exception, but cmd2 handles it
    run_cmd(base_app, 'edit')
    out, err = capsys.readouterr()

    expected = _expected_no_editor_error()
    assert normalize(str(err)) == expected

def test_edit_file(base_app, request, monkeypatch):
    # Set a fake editor just to make sure we have one.  We aren't really going to call it due to the mock
    base_app.editor = 'fooedit'

    # Mock out the os.system call so we don't actually open an editor
    m = mock.MagicMock(name='system')
    monkeypatch.setattr("os.system", m)

    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'script.txt')

    run_cmd(base_app, 'edit {}'.format(filename))

    # We think we have an editor, so should expect a system call
    m.assert_called_once_with('"{}" "{}"'.format(base_app.editor, filename))

def test_edit_file_with_spaces(base_app, request, monkeypatch):
    # Set a fake editor just to make sure we have one.  We aren't really going to call it due to the mock
    base_app.editor = 'fooedit'

    # Mock out the os.system call so we don't actually open an editor
    m = mock.MagicMock(name='system')
    monkeypatch.setattr("os.system", m)

    test_dir = os.path.dirname(request.module.__file__)
    filename = os.path.join(test_dir, 'my commands.txt')

    run_cmd(base_app, 'edit "{}"'.format(filename))

    # We think we have an editor, so should expect a system call
    m.assert_called_once_with('"{}" "{}"'.format(base_app.editor, filename))

def test_edit_blank(base_app, monkeypatch):
    # Set a fake editor just to make sure we have one.  We aren't really going to call it due to the mock
    base_app.editor = 'fooedit'

    # Mock out the os.system call so we don't actually open an editor
    m = mock.MagicMock(name='system')
    monkeypatch.setattr("os.system", m)

    run_cmd(base_app, 'edit')

    # We have an editor, so should expect a system call
    m.assert_called_once()


def test_base_py_interactive(base_app):
    # Mock out the InteractiveConsole.interact() call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='interact')
    InteractiveConsole.interact = m

    run_cmd(base_app, "py")

    # Make sure our mock was called once and only once
    m.assert_called_once()


def test_exclude_from_history(base_app, monkeypatch):
    # Mock out the os.system call so we don't actually open an editor
    m = mock.MagicMock(name='system')
    monkeypatch.setattr("os.system", m)

    # Run edit command
    run_cmd(base_app, 'edit')

    # Run history command
    run_cmd(base_app, 'history')

    # Verify that the history is empty
    out = run_cmd(base_app, 'history')
    assert out == []

    # Now run a command which isn't excluded from the history
    run_cmd(base_app, 'help')
    # And verify we have a history now ...
    out = run_cmd(base_app, 'history')
    expected = normalize("""-------------------------[1]
help""")
    assert out == expected


def test_base_cmdloop_with_queue():
    # Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
    app = cmd2.Cmd()
    app.use_rawinput = True
    intro = 'Hello World, this is an intro ...'
    app.cmdqueue.append('quit\n')
    app.stdout = StdOut()

    # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
    testargs = ["prog"]
    expected = intro + '\n'
    with mock.patch.object(sys, 'argv', testargs):
        # Run the command loop with custom intro
        app.cmdloop(intro=intro)
    out = app.stdout.buffer
    assert out == expected


def test_base_cmdloop_without_queue():
    # Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
    app = cmd2.Cmd()
    app.use_rawinput = True
    app.intro = 'Hello World, this is an intro ...'
    app.stdout = StdOut()

    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input', return_value='quit')
    builtins.input = m

    # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
    testargs = ["prog"]
    expected = app.intro + '\n'
    with mock.patch.object(sys, 'argv', testargs):
        # Run the command loop
        app.cmdloop()
    out = app.stdout.buffer
    assert out == expected


def test_cmdloop_without_rawinput():
    # Create a cmd2.Cmd() instance and make sure basic settings are like we want for test
    app = cmd2.Cmd()
    app.use_rawinput = False
    app.echo = False
    app.intro = 'Hello World, this is an intro ...'
    app.stdout = StdOut()

    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input', return_value='quit')
    builtins.input = m

    # Need to patch sys.argv so cmd2 doesn't think it was called with arguments equal to the py.test args
    testargs = ["prog"]
    expected = app.intro + '\n'
    with mock.patch.object(sys, 'argv', testargs):
        # Run the command loop
        app.cmdloop()
    out = app.stdout.buffer
    assert out == expected


class HookFailureApp(cmd2.Cmd):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def postparsing_precmd(self, statement):
        """Simulate precmd hook failure."""
        return True, statement

@pytest.fixture
def hook_failure():
    app = HookFailureApp()
    app.stdout = StdOut()
    return app

def test_precmd_hook_success(base_app):
    out = base_app.onecmd_plus_hooks('help')
    assert out is None


def test_precmd_hook_failure(hook_failure):
    out = hook_failure.onecmd_plus_hooks('help')
    assert out == True


class SayApp(cmd2.Cmd):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_say(self, arg):
        self.poutput(arg)

@pytest.fixture
def say_app():
    app = SayApp()
    app.allow_cli_args = False
    app.stdout = StdOut()
    return app

def test_interrupt_quit(say_app):
    say_app.quit_on_sigint = True

    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input')
    m.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'eof']
    builtins.input = m

    say_app.cmdloop()

    # And verify the expected output to stdout
    out = say_app.stdout.buffer
    assert out == 'hello\n'

def test_interrupt_noquit(say_app):
    say_app.quit_on_sigint = False

    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input')
    m.side_effect = ['say hello', KeyboardInterrupt(), 'say goodbye', 'eof']
    builtins.input = m

    say_app.cmdloop()

    # And verify the expected output to stdout
    out = say_app.stdout.buffer
    assert out == 'hello\n^C\ngoodbye\n'


class ShellApp(cmd2.Cmd):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.default_to_shell = True

@pytest.fixture
def shell_app():
    app = ShellApp()
    app.stdout = StdOut()
    return app

def test_default_to_shell_unknown(shell_app):
    unknown_command = 'zyxcw23'
    out = run_cmd(shell_app, unknown_command)
    assert out == ["*** Unknown syntax: {}".format(unknown_command)]

def test_default_to_shell_good(capsys):
    app = cmd2.Cmd()
    app.default_to_shell = True
    if sys.platform.startswith('win'):
        line = 'dir'
    else:
        line = 'ls'
    statement = app.statement_parser.parse(line)
    retval = app.default(statement)
    assert not retval
    out, err = capsys.readouterr()
    assert out == ''

def test_default_to_shell_failure(capsys):
    app = cmd2.Cmd()
    app.default_to_shell = True
    line = 'ls does_not_exist.xyz'
    statement = app.statement_parser.parse(line)
    retval = app.default(statement)
    assert not retval
    out, err = capsys.readouterr()
    assert out == "*** Unknown syntax: {}\n".format(line)


def test_ansi_prompt_not_esacped(base_app):
    prompt = '(Cmd) '
    assert base_app._surround_ansi_escapes(prompt) == prompt


def test_ansi_prompt_escaped():
    app = cmd2.Cmd()
    color = 'cyan'
    prompt = 'InColor'
    color_prompt = app.colorize(prompt, color)

    readline_hack_start = "\x01"
    readline_hack_end = "\x02"

    readline_safe_prompt = app._surround_ansi_escapes(color_prompt)
    if sys.platform.startswith('win'):
        # colorize() does nothing on Windows due to lack of ANSI color support
        assert prompt == color_prompt
        assert readline_safe_prompt == prompt
    else:
        assert prompt != color_prompt
        assert readline_safe_prompt.startswith(readline_hack_start + app._colorcodes[color][True] + readline_hack_end)
        assert readline_safe_prompt.endswith(readline_hack_start + app._colorcodes[color][False] + readline_hack_end)


class HelpApp(cmd2.Cmd):
    """Class for testing custom help_* methods which override docstring help."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_squat(self, arg):
        """This docstring help will never be shown because the help_squat method overrides it."""
        pass

    def help_squat(self):
        self.stdout.write('This command does diddly squat...\n')

    def do_edit(self, arg):
        """This overrides the edit command and does nothing."""
        pass

    # This command will be in the "undocumented" section of the help menu
    def do_undoc(self, arg):
        pass

@pytest.fixture
def help_app():
    app = HelpApp()
    app.stdout = StdOut()
    return app

def test_custom_command_help(help_app):
    out = run_cmd(help_app, 'help squat')
    expected = normalize('This command does diddly squat...')
    assert out == expected

def test_custom_help_menu(help_app):
    out = run_cmd(help_app, 'help')
    expected = normalize("""
Documented commands (type help <topic>):
========================================
alias  help     load  pyscript  set    shortcuts  unalias
edit   history  py    quit      shell  squat

Undocumented commands:
======================
undoc
""")
    assert out == expected

def test_help_undocumented(help_app):
    out = run_cmd(help_app, 'help undoc')
    expected = normalize('*** No help on undoc')
    assert out == expected

def test_help_overridden_method(help_app):
    out = run_cmd(help_app, 'help edit')
    expected = normalize('This overrides the edit command and does nothing.')
    assert out == expected


class HelpCategoriesApp(cmd2.Cmd):
    """Class for testing custom help_* methods which override docstring help."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    @cmd2.with_category('Some Category')
    def do_diddly(self, arg):
        """This command does diddly"""
        pass

    def do_squat(self, arg):
        """This docstring help will never be shown because the help_squat method overrides it."""
        pass

    def help_squat(self):
        self.stdout.write('This command does diddly squat...\n')

    def do_edit(self, arg):
        """This overrides the edit command and does nothing."""
        pass

    cmd2.categorize((do_squat, do_edit), 'Custom Category')

    # This command will be in the "undocumented" section of the help menu
    def do_undoc(self, arg):
        pass

@pytest.fixture
def helpcat_app():
    app = HelpCategoriesApp()
    app.stdout = StdOut()
    return app

def test_help_cat_base(helpcat_app):
    out = run_cmd(helpcat_app, 'help')
    expected = normalize("""Documented commands (type help <topic>):

Custom Category
===============
edit  squat

Some Category
=============
diddly

Other
=====
alias  help  history  load  py  pyscript  quit  set  shell  shortcuts  unalias

Undocumented commands:
======================
undoc
""")
    assert out == expected

def test_help_cat_verbose(helpcat_app):
    out = run_cmd(helpcat_app, 'help --verbose')
    expected = normalize("""Documented commands (type help <topic>):

Custom Category
================================================================================
edit                This overrides the edit command and does nothing.
squat               This command does diddly squat...

Some Category
================================================================================
diddly              This command does diddly

Other
================================================================================
alias               Define or display aliases
help                List available commands with "help" or detailed help with "help cmd".
history             View, run, edit, and save previously entered commands.
load                Runs commands in script file that is encoded as either ASCII or UTF-8 text.
py                  Invoke python command, shell, or script
pyscript            Runs a python script file inside the console
quit                Exits this application.
set                 Sets a settable parameter or shows current settings of parameters.
shell               Execute a command as if at the OS prompt.
shortcuts           Lists shortcuts (aliases) available.
unalias             Unsets aliases

Undocumented commands:
======================
undoc
""")
    assert out == expected


class SelectApp(cmd2.Cmd):
    def do_eat(self, arg):
        """Eat something, with a selection of sauces to choose from."""
        # Pass in a single string of space-separated selections
        sauce = self.select('sweet salty', 'Sauce? ')
        result = '{food} with {sauce} sauce, yum!'
        result = result.format(food=arg, sauce=sauce)
        self.stdout.write(result + '\n')

    def do_study(self, arg):
        """Learn something, with a selection of subjects to choose from."""
        # Pass in a list of strings for selections
        subject = self.select(['math', 'science'], 'Subject? ')
        result = 'Good luck learning {}!\n'.format(subject)
        self.stdout.write(result)

    def do_procrastinate(self, arg):
        """Waste time in your manner of choice."""
        # Pass in a list of tuples for selections
        leisure_activity = self.select([('Netflix and chill', 'Netflix'), ('Porn', 'WebSurfing')],
                                       'How would you like to procrastinate? ')
        result = 'Have fun procrasinating with {}!\n'.format(leisure_activity)
        self.stdout.write(result)

    def do_play(self, arg):
        """Play your favorite musical instrument."""
        # Pass in an uneven list of tuples for selections
        instrument = self.select([('Guitar', 'Electric Guitar'), ('Drums',)], 'Instrument? ')
        result = 'Charm us with the {}...\n'.format(instrument)
        self.stdout.write(result)

@pytest.fixture
def select_app():
    app = SelectApp()
    app.stdout = StdOut()
    return app

def test_select_options(select_app):
    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input', return_value='2')
    builtins.input = m

    food = 'bacon'
    out = run_cmd(select_app, "eat {}".format(food))
    expected = normalize("""
   1. sweet
   2. salty
{} with salty sauce, yum!
""".format(food))

    # Make sure our mock was called with the expected arguments
    m.assert_called_once_with('Sauce? ')

    # And verify the expected output to stdout
    assert out == expected

def test_select_invalid_option(select_app):
    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input')
    # If side_effect is an iterable then each call to the mock will return the next value from the iterable.
    m.side_effect = ['3', '1']  # First pass and invalid selection, then pass a valid one
    builtins.input = m

    food = 'fish'
    out = run_cmd(select_app, "eat {}".format(food))
    expected = normalize("""
   1. sweet
   2. salty
'3' isn't a valid choice. Pick a number between 1 and 2:
{} with sweet sauce, yum!
""".format(food))

    # Make sure our mock was called exactly twice with the expected arguments
    arg = 'Sauce? '
    calls = [mock.call(arg), mock.call(arg)]
    m.assert_has_calls(calls)

    # And verify the expected output to stdout
    assert out == expected

def test_select_list_of_strings(select_app):
    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input', return_value='2')
    builtins.input = m

    out = run_cmd(select_app, "study")
    expected = normalize("""
   1. math
   2. science
Good luck learning {}!
""".format('science'))

    # Make sure our mock was called with the expected arguments
    m.assert_called_once_with('Subject? ')

    # And verify the expected output to stdout
    assert out == expected

def test_select_list_of_tuples(select_app):
    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input', return_value='2')
    builtins.input = m

    out = run_cmd(select_app, "procrastinate")
    expected = normalize("""
   1. Netflix
   2. WebSurfing
Have fun procrasinating with {}!
""".format('Porn'))

    # Make sure our mock was called with the expected arguments
    m.assert_called_once_with('How would you like to procrastinate? ')

    # And verify the expected output to stdout
    assert out == expected


def test_select_uneven_list_of_tuples(select_app):
    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.MagicMock(name='input', return_value='2')
    builtins.input = m

    out = run_cmd(select_app, "play")
    expected = normalize("""
   1. Electric Guitar
   2. Drums
Charm us with the {}...
""".format('Drums'))

    # Make sure our mock was called with the expected arguments
    m.assert_called_once_with('Instrument? ')

    # And verify the expected output to stdout
    assert out == expected


class HelpNoDocstringApp(cmd2.Cmd):
    greet_parser = argparse.ArgumentParser()
    greet_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")
    @cmd2.with_argparser_and_unknown_args(greet_parser)
    def do_greet(self, opts, arg):
        arg = ''.join(arg)
        if opts.shout:
            arg = arg.upper()
        self.stdout.write(arg + '\n')

def test_help_with_no_docstring(capsys):
    app = HelpNoDocstringApp()
    app.onecmd_plus_hooks('greet -h')
    out, err = capsys.readouterr()
    assert err == ''
    assert out == """usage: greet [-h] [-s]

optional arguments:
  -h, --help   show this help message and exit
  -s, --shout  N00B EMULATION MODE
"""

@pytest.mark.skipif(sys.platform.startswith('win'),
                    reason="utils.which function only used on Mac and Linux")
def test_which_editor_good():
    editor = 'vi'
    path = utils.which(editor)
    # Assert that the vi editor was found because it should exist on all Mac and Linux systems
    assert path

@pytest.mark.skipif(sys.platform.startswith('win'),
                    reason="utils.which function only used on Mac and Linux")
def test_which_editor_bad():
    editor = 'notepad.exe'
    path = utils.which(editor)
    # Assert that the editor wasn't found because no notepad.exe on non-Windows systems ;-)
    assert path is None


class MultilineApp(cmd2.Cmd):
    def __init__(self, *args, **kwargs):
        self.multiline_commands = ['orate']
        super().__init__(*args, **kwargs)

    orate_parser = argparse.ArgumentParser()
    orate_parser.add_argument('-s', '--shout', action="store_true", help="N00B EMULATION MODE")

    @cmd2.with_argparser_and_unknown_args(orate_parser)
    def do_orate(self, opts, arg):
        arg = ''.join(arg)
        if opts.shout:
            arg = arg.upper()
        self.stdout.write(arg + '\n')

@pytest.fixture
def multiline_app():
    app = MultilineApp()
    app.stdout = StdOut()
    return app

def test_multiline_complete_empty_statement_raises_exception(multiline_app):
    with pytest.raises(cmd2.EmptyStatement):
        multiline_app._complete_statement('')

def test_multiline_complete_statement_without_terminator(multiline_app):
    # Mock out the input call so we don't actually wait for a user's response on stdin when it looks for more input
    m = mock.MagicMock(name='input', return_value='\n')
    builtins.input = m

    command = 'orate'
    args = 'hello world'
    line = '{} {}'.format(command, args)
    statement = multiline_app._complete_statement(line)
    assert statement == args
    assert statement.command == command


def test_clipboard_failure(base_app, capsys):
    # Force cmd2 clipboard to be disabled
    base_app.can_clip = False

    # Redirect command output to the clipboard when a clipboard isn't present
    base_app.onecmd_plus_hooks('help > ')

    # Make sure we got the error output
    out, err = capsys.readouterr()
    assert out == ''
    assert "Cannot redirect to paste buffer; install 'pyperclip' and re-run to enable" in err


class CommandResultApp(cmd2.Cmd):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_affirmative(self, arg):
        self._last_result = cmd2.CommandResult(arg, data=True)

    def do_negative(self, arg):
        self._last_result = cmd2.CommandResult(arg)

@pytest.fixture
def commandresult_app():
    app = CommandResultApp()
    app.stdout = StdOut()
    return app

def test_commandresult_truthy(commandresult_app):
    arg = 'foo'
    run_cmd(commandresult_app, 'affirmative {}'.format(arg))
    assert commandresult_app._last_result
    assert commandresult_app._last_result == cmd2.CommandResult(arg, data=True)

def test_commandresult_falsy(commandresult_app):
    arg = 'bar'
    run_cmd(commandresult_app, 'negative {}'.format(arg))
    assert not commandresult_app._last_result
    assert commandresult_app._last_result == cmd2.CommandResult(arg)


def test_is_text_file_bad_input(base_app):
    # Test with a non-existent file
    file_is_valid = utils.is_text_file('does_not_exist.txt')
    assert not file_is_valid

    # Test with a directory
    dir_is_valid = utils.is_text_file('.')
    assert not dir_is_valid


def test_eof(base_app):
    # Only thing to verify is that it returns True
    assert base_app.do_eof('dont care')

def test_eos(base_app):
    sdir = 'dummy_dir'
    base_app._script_dir.append(sdir)
    assert len(base_app._script_dir) == 1

    # Assert that it does NOT return true
    assert not base_app.do_eos('dont care')

    # And make sure it reduced the length of the script dir list
    assert len(base_app._script_dir) == 0

def test_echo(capsys):
    app = cmd2.Cmd()
    # Turn echo on and pre-stage some commands in the queue, simulating like we are in the middle of a script
    app.echo = True
    command = 'help history'
    app.cmdqueue = [command, 'quit', 'eos']
    app._script_dir.append('some_dir')

    assert app._current_script_dir is not None

    # Run the inner _cmdloop
    app._cmdloop()

    out, err = capsys.readouterr()

    # Check the output
    assert app.cmdqueue == []
    assert app._current_script_dir is None
    assert out.startswith('{}{}\n'.format(app.prompt, command) + HELP_HISTORY.split()[0])

def test_pseudo_raw_input_tty_rawinput_true():
    # use context managers so original functions get put back when we are done
    # we dont use decorators because we need m_input for the assertion
    with mock.patch('sys.stdin.isatty', mock.MagicMock(name='isatty', return_value=True)):
        with mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError])) as m_input:
            # run the cmdloop, which should pull input from our mocks
            app = cmd2.Cmd()
            app.use_rawinput = True
            app._cmdloop()
            # because we mocked the input() call, we won't get the prompt
            # or the name of the command in the output, so we can't check
            # if its there. We assume that if input got called twice, once
            # for the 'set' command, and once for the 'quit' command,
            # that the rest of it worked
            assert m_input.call_count == 2

def test_pseudo_raw_input_tty_rawinput_false():
    # gin up some input like it's coming from a tty
    fakein = io.StringIO(u'{}'.format('set\n'))
    mtty = mock.MagicMock(name='isatty', return_value=True)
    fakein.isatty = mtty
    mreadline = mock.MagicMock(name='readline', wraps=fakein.readline)
    fakein.readline = mreadline

    # run the cmdloop, telling it where to get input from
    app = cmd2.Cmd(stdin=fakein)
    app.use_rawinput = False
    app._cmdloop()

    # because we mocked the readline() call, we won't get the prompt
    # or the name of the command in the output, so we can't check
    # if its there. We assume that if readline() got called twice, once
    # for the 'set' command, and once for the 'quit' command,
    # that the rest of it worked
    assert mreadline.call_count == 2

# the next helper function and two tests check for piped
# input when use_rawinput is True.
def piped_rawinput_true(capsys, echo, command):
    app = cmd2.Cmd()
    app.use_rawinput = True
    app.echo = echo
    # run the cmdloop, which should pull input from our mock
    app._cmdloop()
    out, err = capsys.readouterr()
    return (app, out)

# using the decorator puts the original input function back when this unit test returns
@mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError]))
def test_pseudo_raw_input_piped_rawinput_true_echo_true(capsys):
    command = 'set'
    app, out = piped_rawinput_true(capsys, True, command)
    out = out.splitlines()
    assert out[0] == '{}{}'.format(app.prompt, command)
    assert out[1].startswith('colors:')

# using the decorator puts the original input function back when this unit test returns
@mock.patch('builtins.input', mock.MagicMock(name='input', side_effect=['set', EOFError]))
def test_pseudo_raw_input_piped_rawinput_true_echo_false(capsys):
    command = 'set'
    app, out = piped_rawinput_true(capsys, False, command)
    firstline = out.splitlines()[0]
    assert firstline.startswith('colors:')
    assert not '{}{}'.format(app.prompt, command) in out

# the next helper function and two tests check for piped
# input when use_rawinput=False
def piped_rawinput_false(capsys, echo, command):
    fakein = io.StringIO(u'{}'.format(command))
    # run the cmdloop, telling it where to get input from
    app = cmd2.Cmd(stdin=fakein)
    app.use_rawinput = False
    app.echo = echo
    app._cmdloop()
    out, err = capsys.readouterr()
    return (app, out)

def test_pseudo_raw_input_piped_rawinput_false_echo_true(capsys):
    command = 'set'
    app, out = piped_rawinput_false(capsys, True, command)
    out = out.splitlines()
    assert out[0] == '{}{}'.format(app.prompt, command)
    assert out[1].startswith('colors:')

def test_pseudo_raw_input_piped_rawinput_false_echo_false(capsys):
    command = 'set'
    app, out = piped_rawinput_false(capsys, False, command)
    firstline = out.splitlines()[0]
    assert firstline.startswith('colors:')
    assert not '{}{}'.format(app.prompt, command) in out

#
# other input tests
def test_raw_input(base_app):
    base_app.use_raw_input = True
    fake_input = 'quit'

    # Mock out the input call so we don't actually wait for a user's response on stdin
    m = mock.Mock(name='input', return_value=fake_input)
    builtins.input = m

    line = base_app.pseudo_raw_input('(cmd2)')
    assert line == fake_input

def test_stdin_input():
    app = cmd2.Cmd()
    app.use_rawinput = False
    fake_input = 'quit'

    # Mock out the readline call so we don't actually read from stdin
    m = mock.Mock(name='readline', return_value=fake_input)
    app.stdin.readline = m

    line = app.pseudo_raw_input('(cmd2)')
    assert line == fake_input

def test_empty_stdin_input():
    app = cmd2.Cmd()
    app.use_rawinput = False
    fake_input = ''

    # Mock out the readline call so we don't actually read from stdin
    m = mock.Mock(name='readline', return_value=fake_input)
    app.stdin.readline = m

    line = app.pseudo_raw_input('(cmd2)')
    assert line == 'eof'


def test_poutput_string(base_app):
    msg = 'This is a test'
    base_app.poutput(msg)
    out = base_app.stdout.buffer
    expected = msg + '\n'
    assert out == expected

def test_poutput_zero(base_app):
    msg = 0
    base_app.poutput(msg)
    out = base_app.stdout.buffer
    expected = str(msg) + '\n'
    assert out == expected

def test_poutput_empty_string(base_app):
    msg = ''
    base_app.poutput(msg)
    out = base_app.stdout.buffer
    expected = msg
    assert out == expected

def test_poutput_none(base_app):
    msg = None
    base_app.poutput(msg)
    out = base_app.stdout.buffer
    expected = ''
    assert out == expected


def test_alias(base_app, capsys):
    # Create the alias
    out = run_cmd(base_app, 'alias fake pyscript')
    assert out == normalize("Alias 'fake' created")

    # Use the alias
    run_cmd(base_app, 'fake')
    out, err = capsys.readouterr()
    assert "pyscript command requires at least 1 argument" in err

    # See a list of aliases
    out = run_cmd(base_app, 'alias')
    assert out == normalize('alias fake pyscript')

    # Lookup the new alias
    out = run_cmd(base_app, 'alias fake')
    assert out == normalize('alias fake pyscript')

def test_alias_lookup_invalid_alias(base_app, capsys):
    # Lookup invalid alias
    out = run_cmd(base_app, 'alias invalid')
    out, err = capsys.readouterr()
    assert "not found" in err

def test_unalias(base_app):
    # Create an alias
    run_cmd(base_app, 'alias fake pyscript')

    # Remove the alias
    out = run_cmd(base_app, 'unalias fake')
    assert out == normalize("Alias 'fake' cleared")

def test_unalias_all(base_app):
    out = run_cmd(base_app, 'unalias -a')
    assert out == normalize("All aliases cleared")

def test_unalias_non_existing(base_app, capsys):
    run_cmd(base_app, 'unalias fake')
    out, err = capsys.readouterr()
    assert "does not exist" in err

@pytest.mark.parametrize('alias_name', [
    '">"',
    '"no>pe"',
    '"no spaces"',
    '"nopipe|"',
    '"noterm;"',
    'noembedded"quotes',
])
def test_create_invalid_alias(base_app, alias_name, capsys):
    run_cmd(base_app, 'alias {} help'.format(alias_name))
    out, err = capsys.readouterr()
    assert "can not contain" in err

def test_ppaged(base_app):
    msg = 'testing...'
    end = '\n'
    base_app.ppaged(msg)
    out = base_app.stdout.buffer
    assert out == msg + end

# we override cmd.parseline() so we always get consistent
# command parsing by parent methods we don't override
# don't need to test all the parsing logic here, because
# parseline just calls StatementParser.parse_command_only()
def test_parseline_empty(base_app):
    statement = ''
    command, args, line = base_app.parseline(statement)
    assert not command
    assert not args
    assert not line

def test_parseline(base_app):
    statement = " command with 'partially completed quotes  "
    command, args, line = base_app.parseline(statement)
    assert command == 'command'
    assert args == "with 'partially completed quotes"
    assert line == statement.strip()


def test_readline_remove_history_item(base_app):
    from cmd2.rl_utils import readline
    assert readline.get_current_history_length() == 0
    readline.add_history('this is a test')
    assert readline.get_current_history_length() == 1
    readline.remove_history_item(0)
    assert readline.get_current_history_length() == 0

def test_onecmd_raw_str_continue(base_app):
    line = "help"
    stop = base_app.onecmd(line)
    out = base_app.stdout.buffer
    assert not stop
    assert out.strip() == BASE_HELP.strip()

def test_onecmd_raw_str_quit(base_app):
    line = "quit"
    stop = base_app.onecmd(line)
    out = base_app.stdout.buffer
    assert stop
    assert out == ''

