Merge pull request #1642 from ZyX-I/wm-daemon

Use powerline daemon for running awesome bindings
This commit is contained in:
Nikolai Aleksandrovich Pavlov 2017-04-01 15:31:42 +03:00 committed by GitHub
commit 5fa504118e
21 changed files with 503 additions and 120 deletions

View File

@ -215,6 +215,13 @@ Common configuration is a subdictionary that is a value of ``ext`` key in
All components are enabled by default.
.. _config-ext-update_interval:
``update_interval``
Determines how often WM status bars need to be updated, in seconds. Only
valid for WM extensions which use ``powerline-daemon``. Defaults to
2 seconds.
.. _config-colors:
Color definitions

View File

@ -419,6 +419,11 @@ def _get_log_keys(common_config):
))
DEFAULT_UPDATE_INTERVAL = 2
'''Default value for :ref:`update_interval <config-ext-update_interval>`
'''
class Powerline(object):
'''Main powerline class, entrance point for all powerline uses. Sets
powerline up and loads the configuration.
@ -514,6 +519,7 @@ class Powerline(object):
self.setup_args = ()
self.setup_kwargs = {}
self.imported_modules = set()
self.update_interval = DEFAULT_UPDATE_INTERVAL
get_encoding = staticmethod(get_preferred_output_encoding)
'''Get encoding used by the current application
@ -638,6 +644,7 @@ class Powerline(object):
or self.ext_config.get('local_themes') != self.prev_ext_config.get('local_themes')
):
self.renderer_options['local_themes'] = self.get_local_themes(self.ext_config.get('local_themes'))
self.update_interval = self.ext_config.get('update_interval', 2)
load_colorscheme = (
load_colorscheme
or not self.prev_ext_config

View File

@ -4,38 +4,17 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import sys
from time import sleep
from subprocess import Popen, PIPE
from powerline import Powerline
from powerline.lib.monotonic import monotonic
powerline = Powerline('wm', renderer_module='pango_markup')
powerline.update_renderer()
try:
interval = float(sys.argv[1])
except IndexError:
interval = 2
from powerline.bindings.wm import DEFAULT_UPDATE_INTERVAL
from powerline.bindings.wm.awesome import run
def read_to_log(pl, client):
for line in client.stdout:
if line:
pl.info(line, prefix='awesome-client')
for line in client.stderr:
if line:
pl.error(line, prefix='awesome-client')
if client.wait():
pl.error('Client exited with {0}', client.returncode, prefix='awesome')
def main():
try:
interval = float(sys.argv[1])
except IndexError:
interval = DEFAULT_UPDATE_INTERVAL
run(interval=interval)
while True:
start_time = monotonic()
s = powerline.render(side='right')
request = 'powerline_widget:set_markup(\'' + s.replace('\\', '\\\\').replace('\'', '\\\'') + '\')\n'
client = Popen(['awesome-client'], shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE)
client.stdin.write(request.encode('utf-8'))
client.stdin.close()
read_to_log(powerline.pl, client)
sleep(max(interval - (monotonic() - start_time), 0.1))
if __name__ == '__main__':
main()

View File

@ -6,6 +6,5 @@ powerline_widget:set_align('right')
function powerline(mode, widget) end
bindings_path = string.gsub(debug.getinfo(1).source:match('@(.*)$'), '/[^/]+$', '')
powerline_cmd = bindings_path .. '/powerline-awesome.py'
awful.util.spawn_with_shell('ps -C powerline-awesome.py || ' .. powerline_cmd)
awful.util.spawn_with_shell('powerline-daemon -q')
awful.util.spawn_with_shell('powerline wm.awesome')

View File

@ -11,6 +11,7 @@ from argparse import ArgumentParser
from powerline.lemonbar import LemonbarPowerline
from powerline.lib.encoding import get_unicode_writer
from powerline.bindings.wm import DEFAULT_UPDATE_INTERVAL
if __name__ == '__main__':
@ -29,7 +30,7 @@ if __name__ == '__main__':
def render(reschedule=False):
if reschedule:
Timer(0.5, render, kwargs={'reschedule': True}).start()
Timer(DEFAULT_UPDATE_INTERVAL, render, kwargs={'reschedule': True}).start()
global lock
with lock:

View File

@ -99,7 +99,7 @@ class EmptyArgs(object):
def init_tmux_environment(pl, args, set_tmux_environment=set_tmux_environment):
'''Initialize tmux environment from tmux configuration
'''
powerline = ShellPowerline(finish_args(os.environ, EmptyArgs('tmux', args.config_path)))
powerline = ShellPowerline(finish_args(None, os.environ, EmptyArgs('tmux', args.config_path)))
# TODO Move configuration files loading out of Powerline object and use it
# directly
powerline.update_renderer()

View File

@ -5,6 +5,10 @@ import re
from powerline.theme import requires_segment_info
from powerline.lib.shell import run_cmd
from powerline.bindings.wm.awesome import AwesomeThread
DEFAULT_UPDATE_INTERVAL = 0.5
conn = None
@ -36,3 +40,8 @@ def get_connected_xrandr_outputs(pl):
return (match.groupdict() for match in XRANDR_OUTPUT_RE.finditer(
run_cmd(pl, ['xrandr', '-q'])
))
wm_threads = {
'awesome': AwesomeThread,
}

View File

@ -0,0 +1,59 @@
# vim:fileencoding=utf-8:noet
from __future__ import (unicode_literals, division, absolute_import, print_function)
import sys
from threading import Thread, Event
from time import sleep
from subprocess import Popen, PIPE
from powerline import Powerline
from powerline.lib.monotonic import monotonic
def read_to_log(pl, client):
for line in client.stdout:
if line:
pl.info(line, prefix='awesome-client')
for line in client.stderr:
if line:
pl.error(line, prefix='awesome-client')
if client.wait():
pl.error('Client exited with {0}', client.returncode, prefix='awesome')
def run(thread_shutdown_event=None, pl_shutdown_event=None, pl_config_loader=None,
interval=None):
powerline = Powerline(
'wm',
renderer_module='pango_markup',
shutdown_event=pl_shutdown_event,
config_loader=pl_config_loader,
)
powerline.update_renderer()
if not thread_shutdown_event:
thread_shutdown_event = powerline.shutdown_event
while not thread_shutdown_event.is_set():
# powerline.update_interval may change over time
used_interval = interval or powerline.update_interval
start_time = monotonic()
s = powerline.render(side='right')
request = 'powerline_widget:set_markup(\'' + s.translate({'\'': '\\\'', '\\': '\\\\'}) + '\')\n'
client = Popen(['awesome-client'], shell=False, stdout=PIPE, stderr=PIPE, stdin=PIPE)
client.stdin.write(request.encode('utf-8'))
client.stdin.close()
read_to_log(powerline.pl, client)
thread_shutdown_event.wait(max(used_interval - (monotonic() - start_time), 0.1))
class AwesomeThread(Thread):
__slots__ = ('powerline_shutdown_event',)
def __init__(self, **kwargs):
super(AwesomeThread, self).__init__()
self.powerline_run_kwargs = kwargs
def run(self):
run(**self.powerline_run_kwargs)

View File

@ -25,6 +25,11 @@ _powerline_tmux_pane() {
echo "${TMUX_PANE:-`tmux display -p "#D"`}" | tr -d ' %'
}
_powerline_tmux_pane() {
local -x TMUX="$_POWERLINE_TMUX"
echo "${TMUX_PANE:-`tmux display -p "#D"`}" | tr -d ' %'
}
_powerline_init_tmux_support() {
emulate -L zsh
if test -n "$TMUX" && tmux refresh -S &>/dev/null ; then

View File

@ -11,6 +11,7 @@ from powerline.lib.overrides import parsedotval, parse_override_var
from powerline.lib.dict import mergeargs
from powerline.lib.encoding import get_preferred_arguments_encoding
from powerline.lib.unicode import u, unicode
from powerline.bindings.wm import wm_threads
if sys.version_info < (3,):
@ -23,7 +24,7 @@ else:
return s
def finish_args(environ, args):
def finish_args(parser, environ, args, is_daemon=False):
'''Do some final transformations
Transforms ``*_override`` arguments into dictionaries, adding overrides from
@ -61,7 +62,13 @@ def finish_args(environ, args):
[path for path in environ.get('POWERLINE_CONFIG_PATHS', '').split(':') if path]
+ (args.config_path or [])
)
args.side = args.side[0]
if args.ext[0].startswith('wm.'):
if not is_daemon:
parser.error('WM bindings must be used with daemon only')
elif args.ext[0][3:] not in wm_threads:
parser.error('WM binding not found')
elif not args.side:
parser.error('expected one argument')
return args
@ -77,15 +84,16 @@ def get_argparser(ArgumentParser=argparse.ArgumentParser):
parser.add_argument(
'ext', nargs=1,
help='Extension: application for which powerline command is launched '
'(usually `shell\' or `tmux\').'
'(usually `shell\' or `tmux\'). Also supports `wm.\' extensions: '
+ ', '.join(('`wm.' + key + '\'' for key in wm_threads.keys())) + '.'
)
parser.add_argument(
'side', nargs=1, choices=('left', 'right', 'above', 'aboveleft'),
'side', nargs='?', choices=('left', 'right', 'above', 'aboveleft'),
help='Side: `left\' and `right\' represent left and right side '
'respectively, `above\' emits lines that are supposed to be printed '
'just above the prompt and `aboveleft\' is like concatenating '
'`above\' with `left\' with the exception that only one Python '
'instance is used in this case.'
'instance is used in this case. May be omitted for `wm.*\' extensions.'
)
parser.add_argument(
'-r', '--renderer-module', metavar='MODULE', type=str,

View File

@ -46,7 +46,8 @@
},
"wm": {
"colorscheme": "default",
"theme": "default"
"theme": "default",
"update_interval": 2
}
}
}

View File

@ -130,7 +130,8 @@ main_spec = (Spec(
local_themes=Spec().unknown_spec(
Spec().re('^[0-9A-Za-z-]+$'),
ext_theme_spec()
).optional()
).optional(),
update_interval=Spec().cmp('gt', 0.0).optional(),
).optional(),
).unknown_spec(
check_ext,

View File

@ -6,6 +6,9 @@ import socket
import os
import errno
import sys
import fcntl
import atexit
import stat
from argparse import ArgumentParser
from select import select
@ -13,21 +16,21 @@ from signal import signal, SIGTERM
from time import sleep
from functools import partial
from io import BytesIO
from threading import Event
from itertools import chain
from logging import StreamHandler
from powerline.shell import ShellPowerline
from powerline.commands.main import finish_args, write_output
from powerline.lib.monotonic import monotonic
from powerline.lib.encoding import get_preferred_output_encoding, get_preferred_arguments_encoding, get_unicode_writer
from powerline.bindings.wm import wm_threads
from powerline.commands.main import get_argparser as get_main_argparser
from powerline.commands.daemon import get_argparser as get_daemon_argparser
is_daemon = False
use_filesystem = not sys.platform.lower().startswith('linux')
address = None
pidfile = None
USE_FILESYSTEM = not sys.platform.lower().startswith('linux')
class NonInteractiveArgParser(ArgumentParser):
@ -44,31 +47,48 @@ class NonInteractiveArgParser(ArgumentParser):
raise Exception(self.format_usage())
parser = get_main_argparser(NonInteractiveArgParser)
EOF = b'EOF\0\0'
powerlines = {}
logger = None
config_loader = None
home = os.path.expanduser('~')
class State(object):
__slots__ = ('powerlines', 'logger', 'config_loader', 'started_wm_threads',
'ts_shutdown_event')
def __init__(self, **kwargs):
self.logger = None
self.config_loader = None
self.started_wm_threads = {}
self.powerlines = {}
self.ts_shutdown_event = Event()
class PowerlineDaemon(ShellPowerline):
HOME = os.path.expanduser('~')
class NonDaemonShellPowerline(ShellPowerline):
def get_log_handler(self):
if not is_daemon:
import logging
return logging.StreamHandler()
return super(PowerlineDaemon, self).get_log_handler()
return StreamHandler()
def render(args, environ, cwd):
global logger
global config_loader
cwd = cwd or environ.get('PWD', '/')
def start_wm(args, environ, cwd, is_daemon, state):
wm_name = args.ext[0][3:]
if wm_name in state.started_wm_threads:
return b''
thread_shutdown_event = Event()
thread = wm_threads[wm_name](
thread_shutdown_event=thread_shutdown_event,
pl_shutdown_event=state.ts_shutdown_event,
pl_config_loader=state.config_loader,
)
thread.start()
state.started_wm_threads[wm_name] = (thread, thread_shutdown_event)
return b''
def render(args, environ, cwd, is_daemon, state):
segment_info = {
'getcwd': lambda: cwd,
'home': environ.get('HOME', home),
'home': environ.get('HOME', HOME),
'environ': environ,
'args': args,
}
@ -82,22 +102,24 @@ def render(args, environ, cwd):
environ.get('POWERLINE_CONFIG_OVERRIDES', ''),
environ.get('POWERLINE_CONFIG_PATHS', ''),
)
finish_args(environ, args)
PowerlineClass = ShellPowerline if is_daemon else NonDaemonShellPowerline
powerline = None
try:
powerline = powerlines[key]
powerline = state.powerlines[key]
except KeyError:
try:
powerline = powerlines[key] = PowerlineDaemon(
powerline = state.powerlines[key] = PowerlineClass(
args,
logger=logger,
config_loader=config_loader,
logger=state.logger,
config_loader=state.config_loader,
run_once=False,
shutdown_event=state.ts_shutdown_event,
)
if logger is None:
logger = powerline.logger
if config_loader is None:
config_loader = powerline.config_loader
if state.logger is None:
state.logger = powerline.logger
if state.config_loader is None:
state.config_loader = powerline.config_loader
except SystemExit:
# Somebody thought raising system exit was a good idea,
return ''
@ -168,23 +190,30 @@ def safe_bytes(o, encoding=get_preferred_output_encoding()):
return safe_bytes(str(e), encoding)
def parse_args(req, encoding=get_preferred_arguments_encoding()):
def parse_args(req, parser, encoding=get_preferred_arguments_encoding()):
args = [x.decode(encoding) for x in req.split(b'\0') if x]
numargs = int(args[0], 16)
shell_args = parser.parse_args(args[1:numargs + 1])
cwd = args[numargs + 1]
environ = dict(((k, v) for k, v in (x.partition('=')[0::2] for x in args[numargs + 2:])))
cwd = cwd or environ.get('PWD', '/')
return shell_args, environ, cwd
def do_render(req):
def get_answer(req, is_daemon, argparser, state):
try:
return safe_bytes(render(*parse_args(req)))
args, environ, cwd = parse_args(req, argparser)
finish_args(argparser, environ, args, is_daemon=True)
if args.ext[0].startswith('wm.'):
return safe_bytes(start_wm(args, environ, cwd, is_daemon, state))
else:
return safe_bytes(render(args, environ, cwd, is_daemon, state))
except Exception as e:
return safe_bytes(str(e))
def do_one(sock, read_sockets, write_sockets, result_map):
def do_one(sock, read_sockets, write_sockets, result_map, is_daemon, argparser,
state):
r, w, e = select(
tuple(read_sockets) + (sock,),
tuple(write_sockets),
@ -214,7 +243,7 @@ def do_one(sock, read_sockets, write_sockets, result_map):
if req == EOF:
raise SystemExit(0)
elif req:
ans = do_render(req)
ans = get_answer(req, is_daemon, argparser, state)
result_map[s] = ans
write_sockets.add(s)
else:
@ -230,17 +259,61 @@ def do_one(sock, read_sockets, write_sockets, result_map):
s.close()
def main_loop(sock):
def shutdown(sock, read_sockets, write_sockets, state):
'''Perform operations necessary for nicely shutting down daemon
Specifically it
#. Closes all sockets.
#. Notifies segments based on
:py:class:`powerline.lib.threaded.ThreadedSegment` and WM-specific
threads that daemon is shutting down.
#. Waits for threads to finish, but no more then 2 seconds total.
#. Waits so that total execution time of this function is 2 seconds in order
to allow ThreadedSegments to finish.
'''
total_wait_time = 2
shutdown_start_time = monotonic()
for s in chain((sock,), read_sockets, write_sockets):
s.close()
# Notify ThreadedSegments
state.ts_shutdown_event.set()
for thread, shutdown_event in state.started_wm_threads.values():
shutdown_event.set()
for thread, shutdown_event in state.started_wm_threads.values():
wait_time = total_wait_time - (monotonic() - shutdown_start_time)
if wait_time > 0:
thread.join(wait_time)
wait_time = total_wait_time - (monotonic() - shutdown_start_time)
sleep(wait_time)
def main_loop(sock, is_daemon):
sock.listen(128)
sock.setblocking(0)
read_sockets, write_sockets = set(), set()
result_map = {}
parser = get_main_argparser(NonInteractiveArgParser)
state = State()
try:
while True:
do_one(sock, read_sockets, write_sockets, result_map)
except KeyboardInterrupt:
raise SystemExit(0)
try:
while True:
do_one(
sock, read_sockets, write_sockets, result_map,
is_daemon=is_daemon,
argparser=parser,
state=state,
)
except KeyboardInterrupt:
raise SystemExit(0)
except SystemExit as e:
shutdown(sock, read_sockets, write_sockets, state)
raise e
return 0
@ -249,10 +322,10 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull):
pid = os.fork()
if pid > 0:
# exit first parent
sys.exit(0)
raise SystemExit(0)
except OSError as e:
sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
sys.exit(1)
raise SystemExit(1)
# decouple from parent environment
os.chdir("/")
@ -264,10 +337,10 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull):
pid = os.fork()
if pid > 0:
# exit from second parent
sys.exit(0)
raise SystemExit(0)
except OSError as e:
sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
sys.exit(1)
raise SystemExit(1)
# Redirect standard file descriptors.
si = open(stdin, 'rb')
@ -276,12 +349,11 @@ def daemonize(stdin=os.devnull, stdout=os.devnull, stderr=os.devnull):
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
global is_daemon
is_daemon = True
return True
def check_existing():
if use_filesystem:
def check_existing(address):
if USE_FILESYSTEM:
# We cannot bind if the socket file already exists so remove it, we
# already have a lock on pidfile, so this should be safe.
try:
@ -299,7 +371,7 @@ def check_existing():
return sock
def kill_daemon():
def kill_daemon(address):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
try:
@ -313,7 +385,7 @@ def kill_daemon():
return True
def cleanup_lockfile(fd, *args):
def cleanup_lockfile(pidfile, fd, *args):
try:
# Remove the directory entry for the lock file
os.unlink(pidfile)
@ -326,10 +398,7 @@ def cleanup_lockfile(fd, *args):
raise SystemExit(1)
def lockpidfile():
import fcntl
import atexit
import stat
def lockpidfile(pidfile):
fd = os.open(
pidfile,
os.O_WRONLY | os.O_CREAT,
@ -344,24 +413,25 @@ def lockpidfile():
os.ftruncate(fd, 0)
os.write(fd, ('%d' % os.getpid()).encode('ascii'))
os.fsync(fd)
cleanup = partial(cleanup_lockfile, fd)
cleanup = partial(cleanup_lockfile, pidfile, fd)
signal(SIGTERM, cleanup)
atexit.register(cleanup)
return fd
def main():
global address
global pidfile
parser = get_daemon_argparser()
args = parser.parse_args()
is_daemon = False
address = None
pidfile = None
if args.socket:
address = args.socket
if not use_filesystem:
if not USE_FILESYSTEM:
address = '\0' + address
else:
if use_filesystem:
if USE_FILESYSTEM:
address = '/tmp/powerline-ipc-%d'
else:
# Use the abstract namespace for sockets rather than the filesystem
@ -370,13 +440,13 @@ def main():
address = address % os.getuid()
if use_filesystem:
if USE_FILESYSTEM:
pidfile = address + '.pid'
if args.kill:
if args.foreground or args.replace:
parser.error('--kill and --foreground/--replace cannot be used together')
if kill_daemon():
if kill_daemon(address):
if not args.quiet:
print ('Kill command sent to daemon, if it does not die in a couple of seconds use kill to kill it')
raise SystemExit(0)
@ -386,19 +456,19 @@ def main():
raise SystemExit(1)
if args.replace:
while kill_daemon():
while kill_daemon(address):
if not args.quiet:
print ('Kill command sent to daemon, waiting for daemon to exit, press Ctrl-C to terminate wait and exit')
sleep(2)
if use_filesystem and not args.foreground:
if USE_FILESYSTEM and not args.foreground:
# We must daemonize before creating the locked pidfile, unfortunately,
# this means further print statements are discarded
daemonize()
is_daemon = daemonize()
if use_filesystem:
if USE_FILESYSTEM:
# Create a locked pid file containing the daemons PID
if lockpidfile() is None:
if lockpidfile(pidfile) is None:
if not args.quiet:
sys.stderr.write(
'The daemon is already running. Use %s -k to kill it.\n' % (
@ -406,7 +476,7 @@ def main():
raise SystemExit(1)
# Bind to address or bail if we cannot bind
sock = check_existing()
sock = check_existing(address)
if sock is None:
if not args.quiet:
sys.stderr.write(
@ -414,14 +484,11 @@ def main():
os.path.basename(sys.argv[0])))
raise SystemExit(1)
if args.foreground:
return main_loop(sock)
if not use_filesystem:
if not USE_FILESYSTEM and not args.foreground:
# We daemonize on linux
daemonize()
is_daemon = daemonize()
main_loop(sock)
return main_loop(sock, is_daemon)
if __name__ == '__main__':

View File

@ -23,8 +23,9 @@ else:
if __name__ == '__main__':
args = get_argparser().parse_args()
finish_args(os.environ, args)
parser = get_argparser()
args = parser.parse_args()
finish_args(parser, os.environ, args)
powerline = ShellPowerline(args, run_once=True)
segment_info = {'args': args, 'environ': os.environ}
write_output(args, powerline, segment_info, get_unicode_writer())

193
tests/run_awesome_tests.sh Executable file
View File

@ -0,0 +1,193 @@
#!/bin/sh
. tests/common.sh
enter_suite awesome
TEST_ROOT="$ROOT/tests/awesome"
TEST_PATH="$TEST_ROOT/path"
TEST_STATIC_ROOT="$ROOT/tests/test_awesome"
test -d "$TEST_ROOT" && rm -r "$TEST_ROOT"
mkdir "$TEST_ROOT"
cp -r "$TEST_STATIC_ROOT/path" "$TEST_ROOT"
cp -r "$TEST_STATIC_ROOT/powerline" "$TEST_ROOT"
export PYTHONPATH="$ROOT${PYTHONPATH:+:}$PYTHONPATH"
ln -s "$(which "${PYTHON}")" "$TEST_PATH"/python
ln -s "$(which cat)" "$TEST_PATH"
ln -s "$(which sh)" "$TEST_PATH"
ln -s "$(which env)" "$TEST_PATH"
if which socat ; then
ln -s "$(which socat)" "$TEST_PATH"
fi
for pexe in powerline powerline.sh powerline.py ; do
if test -e scripts/$pexe ; then
ln -s "$PWD/scripts/$pexe" $TEST_ROOT/path
elif test -e client/$pexe ; then
ln -s "$PWD/client/$pexe" $TEST_ROOT/path
elif which $pexe ; then
ln -s "$(which $pexe)" $TEST_ROOT/path
else
continue
fi
if test "x$pexe" != 'xpowerline.sh' || test -e "$TEST_PATH/socat" ; then
POWERLINE_COMMAND="$pexe"
break
fi
done
DEPRECATED_SCRIPT="$ROOT/powerline/bindings/awesome/powerline-awesome.py"
POWERLINE_DAEMON="scripts/powerline-daemon"
run() {
env -i \
LANG=C \
PATH="$TEST_PATH" \
XDG_CONFIG_HOME="$TEST_ROOT" \
XDG_CONFIG_DIRS="$TEST_ROOT/dummy" \
PYTHONPATH="$PYTHONPATH" \
TEST_ROOT="$TEST_ROOT" \
LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
"$@" || true
}
display_log() {
local log_file="$1"
echo "$log_file:"
echo '============================================================'
cat -v "$log_file"
echo
echo '____________________________________________________________'
}
check_log() {
local args_file="$TEST_ROOT/results/args"
local log_file="$TEST_ROOT/results/requests"
local line="$(head -n1 "$log_file")"
local linenum="$(cat "$log_file" | wc -l)"
echo "Number of runs: $linenum (expected approx 5 / 0.5 = 10 runs)"
if test $linenum -lt 5 ; then
fail "log:lt" F "Script was run not enough times: $linenum < 5"
return 1
elif test $linenum -gt 15 ; then
fail "log:gt" E "Script was run too many times: $linenum > 15"
return 1
fi
local expline="powerline_widget:set_markup('<span foreground=\"#303030\"> </span><span foreground=\"#d0d0d0\" background=\"#303030\" font_weight=\"bold\"> default-right </span>')"
if test "x$expline" != "x$line" ; then
echo "Line: '$line'"
echo "Expected: '$expline'"
fail "log:line" F "Unexpected line"
return 1
fi
local ret=0
while test $linenum -gt 0 ; do
echo "$line" >> "$TEST_ROOT/ok"
linenum=$(( linenum - 1 ))
done
if ! diff "$TEST_ROOT/ok" "$log_file" ; then
fail "log:diff" F "Unexpected output"
ret=1
fi
rm "$TEST_ROOT/ok"
return $ret
}
killscript() {
kill -KILL $1 || true
}
if ! test -e "$DEPRECATED_SCRIPT" ; then
# TODO: uncomment when skip is available
# skip "deprecated" "Missing deprecated bar bindings script"
:
else
enter_suite "deprecated"
for args in "" "0.5"; do
rm -rf "$TEST_ROOT/results"
mkdir "$TEST_ROOT/results"
DEPRECATED_LOG="$TEST_ROOT/deprecated.log"
run env \
DEPRECATED_SCRIPT="$DEPRECATED_SCRIPT" \
args="$args" \
DEPRECATED_LOG="$DEPRECATED_LOG" \
TEST_ROOT="$TEST_ROOT" \
sh -c '
echo $$ > "$TEST_ROOT/$args-pid"
exec "$DEPRECATED_SCRIPT" $args > "$DEPRECATED_LOG" 2>&1
' &
sleep 5
killscript "$(cat "$TEST_ROOT/$args-pid")"
rm "$TEST_ROOT/$args-pid"
if test "x$(cat "$DEPRECATED_LOG")" != "x" ; then
display_log "$DEPRECATED_LOG"
fail "output" E "Nonempty $DEPRECATED_SCRIPT output"
fi
rm "$DEPRECATED_LOG"
if ! check_log ; then
display_log "$TEST_ROOT/results/args"
fail "log" F "Checking log failed"
fi
done
exit_suite --continue
fi
enter_suite "awesome"
ADDRESS="powerline-ipc-test-$$"
echo "Powerline address: $ADDRESS"
rm -rf "$TEST_ROOT/results"
mkdir "$TEST_ROOT/results"
run env \
POWERLINE_DAEMON="$POWERLINE_DAEMON" \
TEST_ROOT="$TEST_ROOT" \
ADDRESS="$ADDRESS" \
sh -c '
echo $$ > "$TEST_ROOT/dpid"
exec python "$POWERLINE_DAEMON" --socket $ADDRESS --foreground > "$TEST_ROOT/daemon.log" 2>&1
' &
DPID=$!
sleep 2
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.1" 2>&1
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.2" 2>&1
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.3" 2>&1
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.4" 2>&1
run "$POWERLINE_COMMAND" --socket $ADDRESS wm.awesome > "$TEST_ROOT/output.log.5" 2>&1
for log_file in "$TEST_ROOT"/output.log.* ; do
if test "x$(cat "$log_file")" != "x" ; then
display_log "$log_file"
fail "output" E "Nonempty $POWERLINE_COMMAND output at run ${log_file#*.}"
fi
rm "$log_file"
done
sleep 5
run python "$POWERLINE_DAEMON" --socket $ADDRESS --quiet --kill > "$TEST_ROOT/kill.log" 2>&1
if test "x$(cat "$TEST_ROOT/kill.log")" != "x" ; then
display_log "$TEST_ROOT/kill.log"
fail "daemonlog" E "Nonempty kill log"
fi
rm "$TEST_ROOT/kill.log"
wait $DPID
if test "x$(cat "$TEST_ROOT/daemon.log")" != "x" ; then
display_log "$TEST_ROOT/daemon.log"
fail "daemonlog" E "Nonempty daemon log"
fi
rm "$TEST_ROOT/daemon.log"
if ! check_log ; then
display_log "$TEST_ROOT/results/args"
fail "log" F "Checking log failed"
fi
exit_suite --continue
if ! powerline-lint \
-p "$ROOT/powerline/config_files" \
-p "$TEST_STATIC_ROOT/powerline"
then
fail "lint" F "Checking test config failed"
fi
if test $FAILED -eq 0 ; then
rm -r "$TEST_ROOT"
fi
exit_suite

View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "$@" >> "$TEST_ROOT/results/args"
cat >> "$TEST_ROOT/results/requests"

View File

@ -0,0 +1,7 @@
{
"ext": {
"wm": {
"update_interval": 0.5
}
}
}

View File

@ -0,0 +1,18 @@
{
"segments": {
"left": [
{
"type": "string",
"highlight_groups": ["time"],
"contents": "default-left"
}
],
"right": [
{
"type": "string",
"highlight_groups": ["time"],
"contents": "default-right"
}
]
}
}

View File

@ -0,0 +1,18 @@
{
"segments": {
"left": [
{
"type": "string",
"highlight_groups": ["time"],
"contents": "dvi-left"
}
],
"right": [
{
"type": "string",
"highlight_groups": ["time"],
"contents": "dvi-right"
}
]
}
}

View File

@ -127,7 +127,7 @@ class TestParser(TestCase):
}),
]:
args = parser.parse_args(argv)
finish_args({}, args)
finish_args(parser, {}, args)
for key, val in expargs.items():
self.assertEqual(getattr(args, key), val)
for key, val in args.__dict__.items():

View File

@ -311,7 +311,7 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te
if test $TEST_TYPE = daemon ; then
sh -c '
echo $$ > tests/shell/daemon_pid
$PYTHON ./scripts/powerline-daemon -s$ADDRESS -f >tests/shell/daemon_log 2>&1
exec $PYTHON ./scripts/powerline-daemon -s$ADDRESS -f >tests/shell/daemon_log 2>&1
' &
fi
echo "> Testing $TEST_TYPE"