2014-07-19 11:45:44 -04:00
|
|
|
|
# encoding: utf-8
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import re
|
2015-03-05 05:21:09 -05:00
|
|
|
|
import shutil
|
2014-07-19 11:45:44 -04:00
|
|
|
|
import subprocess
|
|
|
|
|
import tempfile
|
|
|
|
|
import textwrap
|
|
|
|
|
import time
|
|
|
|
|
|
2015-03-05 05:21:09 -05:00
|
|
|
|
from test.constant import (ARR_D, ARR_L, ARR_R, ARR_U, BS, ESC, PYTHON3,
|
|
|
|
|
SEQUENCES)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
def wait_until_file_exists(file_path, times=None, interval=0.01):
|
|
|
|
|
while times is None or times:
|
|
|
|
|
if os.path.exists(file_path):
|
|
|
|
|
return True
|
|
|
|
|
time.sleep(interval)
|
|
|
|
|
if times is not None:
|
|
|
|
|
times -= 1
|
|
|
|
|
return False
|
|
|
|
|
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
def read_text_file(filename):
|
|
|
|
|
"""Reads the contens of a text file."""
|
|
|
|
|
if PYTHON3:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
return open(filename, 'r', encoding='utf-8').read()
|
2014-07-19 11:45:44 -04:00
|
|
|
|
else:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
return open(filename, 'r').read()
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_process_running(pid):
|
|
|
|
|
"""Returns true if a process with pid is running, false otherwise."""
|
2015-01-20 15:26:03 -05:00
|
|
|
|
# from
|
|
|
|
|
# http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid
|
2014-07-19 11:45:44 -04:00
|
|
|
|
try:
|
|
|
|
|
os.kill(pid, 0)
|
|
|
|
|
except OSError:
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
return True
|
|
|
|
|
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
def silent_call(cmd):
|
|
|
|
|
"""Calls 'cmd' and returns the exit value."""
|
|
|
|
|
return subprocess.call(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
|
|
|
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
def create_directory(dirname):
|
|
|
|
|
"""Creates 'dirname' and its parents if it does not exist."""
|
|
|
|
|
try:
|
|
|
|
|
os.makedirs(dirname)
|
|
|
|
|
except OSError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TempFileManager(object):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
|
|
|
|
def __init__(self, name=''):
|
|
|
|
|
self._temp_dir = tempfile.mkdtemp(prefix='UltiSnipsTest_' + name)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
def name_temp(self, file_path):
|
|
|
|
|
return os.path.join(self._temp_dir, file_path)
|
|
|
|
|
|
|
|
|
|
def write_temp(self, file_path, content):
|
|
|
|
|
abs_path = self.name_temp(file_path)
|
|
|
|
|
create_directory(os.path.dirname(abs_path))
|
|
|
|
|
if PYTHON3:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
with open(abs_path, 'w', encoding='utf-8') as f:
|
2014-07-19 11:45:44 -04:00
|
|
|
|
f.write(content)
|
|
|
|
|
else:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
with open(abs_path, 'w') as f:
|
2014-07-19 11:45:44 -04:00
|
|
|
|
f.write(content)
|
|
|
|
|
return abs_path
|
|
|
|
|
|
2015-01-20 15:26:03 -05:00
|
|
|
|
def unique_name_temp(self, suffix='', prefix=''):
|
|
|
|
|
file_handler, abspath = tempfile.mkstemp(
|
|
|
|
|
suffix, prefix, self._temp_dir)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
os.close(file_handler)
|
|
|
|
|
os.remove(abspath)
|
|
|
|
|
return abspath
|
|
|
|
|
|
|
|
|
|
def clear_temp(self):
|
|
|
|
|
shutil.rmtree(self._temp_dir)
|
|
|
|
|
create_directory(self._temp_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VimInterface(TempFileManager):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
|
|
|
|
def __init__(self, name=''):
|
2014-07-19 11:45:44 -04:00
|
|
|
|
TempFileManager.__init__(self, name)
|
|
|
|
|
|
|
|
|
|
def get_buffer_data(self):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
buffer_path = self.unique_name_temp(prefix='buffer_')
|
|
|
|
|
self.send(ESC + ':w! %s\n' % buffer_path)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
if wait_until_file_exists(buffer_path, 50):
|
|
|
|
|
return read_text_file(buffer_path)[:-1]
|
|
|
|
|
|
|
|
|
|
def send(self, s):
|
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
def launch(self, config=[]):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
pid_file = self.name_temp('vim.pid')
|
|
|
|
|
done_file = self.name_temp('loading_done')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
if os.path.exists(done_file):
|
|
|
|
|
os.remove(done_file)
|
|
|
|
|
|
|
|
|
|
post_config = []
|
2015-01-20 15:26:03 -05:00
|
|
|
|
post_config.append('%s << EOF' % ('py3' if PYTHON3 else 'py'))
|
|
|
|
|
post_config.append('import vim')
|
|
|
|
|
post_config.append(
|
|
|
|
|
"with open('%s', 'w') as pid_file: pid_file.write(vim.eval('getpid()'))" %
|
|
|
|
|
pid_file)
|
|
|
|
|
post_config.append(
|
|
|
|
|
"with open('%s', 'w') as done_file: pass" %
|
|
|
|
|
done_file)
|
|
|
|
|
post_config.append('EOF')
|
|
|
|
|
|
|
|
|
|
config_path = self.write_temp('vim_config.vim',
|
|
|
|
|
textwrap.dedent(os.linesep.join(config + post_config) + '\n'))
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
# Note the space to exclude it from shell history.
|
|
|
|
|
self.send(""" vim -u %s\r\n""" % config_path)
|
|
|
|
|
wait_until_file_exists(done_file)
|
2015-01-20 15:26:03 -05:00
|
|
|
|
self._vim_pid = int(open(pid_file, 'r').read())
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
def leave_with_wait(self):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
self.send(3 * ESC + ':qa!\n')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
while is_process_running(self._vim_pid):
|
|
|
|
|
time.sleep(.05)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VimInterfaceScreen(VimInterface):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
def __init__(self, session):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
VimInterface.__init__(self, 'Screen')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
self.session = session
|
|
|
|
|
self.need_screen_escapes = 0
|
|
|
|
|
self.detect_parsing()
|
|
|
|
|
|
|
|
|
|
def send(self, s):
|
|
|
|
|
if self.need_screen_escapes:
|
|
|
|
|
# escape characters that are special to some versions of screen
|
|
|
|
|
repl = lambda m: '\\' + m.group(0)
|
2015-01-20 15:26:03 -05:00
|
|
|
|
s = re.sub(r"[$^#\\']", repl, s)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
if PYTHON3:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
s = s.encode('utf-8')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
rv = 0
|
|
|
|
|
if len(s) > 30:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
rv |= silent_call(
|
|
|
|
|
['screen', '-x', self.session, '-X', 'register', 'S', s])
|
|
|
|
|
rv |= silent_call(
|
|
|
|
|
['screen', '-x', self.session, '-X', 'paste', 'S'])
|
2014-07-19 11:45:44 -04:00
|
|
|
|
else:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
rv |= silent_call(
|
|
|
|
|
['screen', '-x', self.session, '-X', 'stuff', s])
|
|
|
|
|
if not rv:
|
|
|
|
|
break
|
2014-07-19 11:45:44 -04:00
|
|
|
|
time.sleep(.2)
|
|
|
|
|
|
|
|
|
|
def detect_parsing(self):
|
|
|
|
|
self.launch()
|
2015-01-20 15:26:03 -05:00
|
|
|
|
# Send a string where the interpretation will depend on version of
|
|
|
|
|
# screen
|
|
|
|
|
string = '$TERM'
|
|
|
|
|
self.send('i' + string + ESC)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
output = self.get_buffer_data()
|
|
|
|
|
# If the output doesn't match the input, need to do additional escaping
|
|
|
|
|
if output != string:
|
|
|
|
|
self.need_screen_escapes = 1
|
|
|
|
|
self.leave_with_wait()
|
|
|
|
|
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
class VimInterfaceTmux(VimInterface):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
def __init__(self, session):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
VimInterface.__init__(self, 'Tmux')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
self.session = session
|
|
|
|
|
self._check_version()
|
|
|
|
|
|
|
|
|
|
def send(self, s):
|
|
|
|
|
# I did not find any documentation on what needs escaping when sending
|
|
|
|
|
# to tmux, but it seems like this is all that is needed for now.
|
|
|
|
|
s = s.replace(';', r'\;')
|
|
|
|
|
|
|
|
|
|
if PYTHON3:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
s = s.encode('utf-8')
|
|
|
|
|
silent_call(['tmux', 'send-keys', '-t', self.session, '-l', s])
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
def _check_version(self):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
stdout, _ = subprocess.Popen(['tmux', '-V'],
|
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
2014-07-19 11:45:44 -04:00
|
|
|
|
if PYTHON3:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
stdout = stdout.decode('utf-8')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
m = re.match(r"tmux (\d+).(\d+)", stdout)
|
|
|
|
|
if not m or not (int(m.group(1)), int(m.group(2))) >= (1, 8):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
raise RuntimeError(
|
|
|
|
|
'Need at least tmux 1.8, you have %s.' %
|
|
|
|
|
stdout.strip())
|
|
|
|
|
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
class VimInterfaceWindows(VimInterface):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
BRACES = re.compile('([}{])')
|
|
|
|
|
WIN_ESCAPES = ['+', '^', '%', '~', '[', ']', '<', '>', '(', ')']
|
2014-07-19 11:45:44 -04:00
|
|
|
|
WIN_REPLACES = [
|
2015-01-20 15:26:03 -05:00
|
|
|
|
(BS, '{BS}'),
|
|
|
|
|
(ARR_L, '{LEFT}'),
|
|
|
|
|
(ARR_R, '{RIGHT}'),
|
|
|
|
|
(ARR_U, '{UP}'),
|
|
|
|
|
(ARR_D, '{DOWN}'),
|
|
|
|
|
('\t', '{TAB}'),
|
|
|
|
|
('\n', '~'),
|
|
|
|
|
(ESC, '{ESC}'),
|
|
|
|
|
|
|
|
|
|
# On my system ` waits for a second keystroke, so `+SPACE = "`". On
|
|
|
|
|
# most systems, `+Space = "` ". I work around this, by sending the host
|
|
|
|
|
# ` as `+_+BS. Awkward, but the only way I found to get this working.
|
|
|
|
|
('`', '`_{BS}'),
|
|
|
|
|
('´', '´_{BS}'),
|
|
|
|
|
('{^}', '{^}_{BS}'),
|
2014-07-19 11:45:44 -04:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.seq_buf = []
|
|
|
|
|
# import windows specific modules
|
2015-01-20 15:26:03 -05:00
|
|
|
|
import win32com.client
|
|
|
|
|
import win32gui
|
2014-07-19 11:45:44 -04:00
|
|
|
|
self.win32gui = win32gui
|
2015-01-20 15:26:03 -05:00
|
|
|
|
self.shell = win32com.client.Dispatch('WScript.Shell')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
def is_focused(self, title=None):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
cur_title = self.win32gui.GetWindowText(
|
|
|
|
|
self.win32gui.GetForegroundWindow())
|
|
|
|
|
if (title or '- GVIM') in cur_title:
|
2014-07-19 11:45:44 -04:00
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def focus(self, title=None):
|
2015-01-20 15:26:03 -05:00
|
|
|
|
if not self.shell.AppActivate(title or '- GVIM'):
|
|
|
|
|
raise Exception('Failed to switch to GVim window')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
|
|
def convert_keys(self, keys):
|
|
|
|
|
keys = self.BRACES.sub(r"{\1}", keys)
|
|
|
|
|
for k in self.WIN_ESCAPES:
|
2015-01-20 15:26:03 -05:00
|
|
|
|
keys = keys.replace(k, '{%s}' % k)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
for f, r in self.WIN_REPLACES:
|
|
|
|
|
keys = keys.replace(f, r)
|
|
|
|
|
return keys
|
|
|
|
|
|
|
|
|
|
def send(self, keys):
|
|
|
|
|
self.seq_buf.append(keys)
|
2015-01-20 15:26:03 -05:00
|
|
|
|
seq = ''.join(self.seq_buf)
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
for f in SEQUENCES:
|
|
|
|
|
if f.startswith(seq) and f != seq:
|
|
|
|
|
return
|
|
|
|
|
self.seq_buf = []
|
|
|
|
|
|
|
|
|
|
seq = self.convert_keys(seq)
|
|
|
|
|
|
|
|
|
|
if not self.is_focused():
|
|
|
|
|
time.sleep(2)
|
|
|
|
|
self.focus()
|
|
|
|
|
if not self.is_focused():
|
|
|
|
|
# This is the only way I can find to stop test execution
|
2015-01-20 15:26:03 -05:00
|
|
|
|
raise KeyboardInterrupt('Failed to focus GVIM')
|
2014-07-19 11:45:44 -04:00
|
|
|
|
|
|
|
|
|
self.shell.SendKeys(seq)
|