From 557570fa09ccc1e2a5b76658bc18d2e04e9d7fb6 Mon Sep 17 00:00:00 2001 From: Alex Wang Date: Wed, 11 Feb 2015 11:08:57 -0800 Subject: [PATCH] ovs-vsctl-bashcomp: Documentation and unit tests. This commit includes the unit tests for ovs-vsctl-bashcomp and refines the ovs-command-bashcomp.INSTALL.md to introduce the bash completion for ovs-vsctl. Signed-off-by: Alex Wang Acked-by: Ben Pfaff --- NEWS | 2 + tests/completion.at | 460 ++++++++++++++++++++++ utilities/automake.mk | 4 +- utilities/ovs-command-bashcomp.INSTALL.md | 90 +++-- 4 files changed, 522 insertions(+), 34 deletions(-) diff --git a/NEWS b/NEWS index 9844ed352..d96be5345 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,7 @@ Post-v2.3.0 --------------------- + - Add bash command-line completion support for ovs-vsctl Please check + utilities/ovs-command-compgen.INSTALL.md for how to use. - The MAC learning feature now includes per-port fairness to mitigate MAC flooding attacks. - New support for a "conjunctive match" OpenFlow extension, which diff --git a/tests/completion.at b/tests/completion.at index ea84b273d..2de4a2c19 100644 --- a/tests/completion.at +++ b/tests/completion.at @@ -326,4 +326,464 @@ AT_CHECK_UNQUOTED([echo "$INPUT" | sed -e 's/[ \t]*$//' | sed -e '/./!d'], INPUT="$(bash ovs-appctl-bashcomp.bash debug ovsdb-tool create TAB 2>&1)" AT_CHECK_UNQUOTED([GET_AVAIL(${INPUT})], [0]) +AT_CLEANUP + + +dnl ---------------------------------------------------------------------- +AT_BANNER([vsctl bashcomp unit tests]) + +m4_define([PREPARE_MATCH_NOSPACE], [ +echo "$@" | tr ' ' '\n' | sed -e '/^$/d' | sort -u +]) + +m4_define([PREPARE_MATCH_SPACE], [ +echo "$@" | tr ' ' '\n' | sed -e '/^$/d' | sed -e 's/$/ /g' | sort -u +]) + +AT_SETUP([vsctl-bashcomp - basic verification]) +AT_SKIP_IF([test -z ${BASH_VERSION+x}]) +OVS_VSWITCHD_START + +# complete ovs-vsctl --db=* [TAB] +TMP="$(ovs-vsctl --commands | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n') +$(ovs-vsctl --options | grep -- '--' | sed -e 's/=.*$/=/g')" +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--db=unix:$OVS_RUNDIR/db.sock "], +[0], [dnl +${MATCH} +]) +# complete ovs-vsctl [TAB] +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test ""], +[0], [dnl +${MATCH} +]) + +# complete on global options. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--dry-run "], +[0], [dnl +${MATCH} +]) +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--dry-run --pretty "], +[0], [dnl +${MATCH} +]) + +# complete on local options. +TMP="$(ovs-vsctl --commands | grep -- '--may-exist' | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n' | grep -v -- '--may-exist')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--may-exist "], +[0], [dnl +${MATCH} +]) + +# +# test !, +, ?, *. +# +# test !. no following arguments are expanded. +TMP="$(ovsdb-client --no-heading list-tables)" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set "], +[0], [dnl +${MATCH} +]) +# test ?. will show completions for both current and following arguments. +ovs-vsctl br-set-external-id br0 bridge-id br0 +MATCH="$(PREPARE_MATCH_SPACE(bridge-id --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], +[0], [dnl +${MATCH} +]) +# test *. argument with this prefix could be completed for zero or more times. +TMP="$(ovs-vsctl --no-heading --columns=_uuid,name list Bridge | tr -d '\"')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP} --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "destroy Bridge "], +[0], [dnl +${MATCH} +]) +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "destroy Bridge br0 "], +[0], [dnl +${MATCH} +]) +# test +. the first time, an argument is required, after that, it becomes '*'. +TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | awk '/key.*value/ { print $1":"; next } { print $1; next }')" +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 "], +[0], [dnl +${MATCH} +]) +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP} --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_key=123 "], +[0], [dnl +${MATCH} +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + + +AT_SETUP([vsctl-bashcomp - argument completion]) +AT_SKIP_IF([test -z ${BASH_VERSION+x}]) +OVS_VSWITCHD_START( + [add-br br1 -- \ + set bridge br1 datapath-type=dummy -- \ + add-br foo -- \ + set bridge foo datapath-type=dummy -- \ + add-br --weird-br_name -- \ + set bridge --weird-br_name datapath-type=dummy -- \ + add-port br0 br0p0 -- set Interface br0p0 type=dummy -- \ + add-port br0 br0p1 -- set Interface br0p1 type=dummy -- \ + add-port br1 br1p0 -- set Interface br1p0 type=dummy -- \ + add-port br1 br1p1 -- set Interface br1p1 type=dummy -- \ + add-port foo bar -- set Interface bar type=dummy ]) +# +# test completion functions defined in '_OVS_VSCTL_ARG_COMPLETION_FUNCS'. +# therein, the different argument prefixes are also tested at the same time. +# +# A space is appended to each output if completion is configured without +# 'nospace' option. +# + + +# +# test: _ovs_vsctl_complete_table +# +TMP="$(ovsdb-client --no-heading list-tables)" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set "], +[0], [dnl +${MATCH} +]) +MATCH="$(PREPARE_MATCH_SPACE(Open_vSwitch))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Open"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_record +# +TMP="$(ovs-vsctl --no-heading --columns=_uuid list Open_vSwitch | tr -d '\"')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Open_vSwitch "], +[0], [dnl +${MATCH} +]) +TMP="$(ovs-vsctl --no-heading --columns=_uuid,name list Bridge | tr -d '\"')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_bridge +# +TMP="$(ovs-vsctl list-br)" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-to-vlan "], +[0], [dnl +${MATCH} +]) +# this also helps check the '_ovs_vsctl_check_startswith_string'. +MATCH="$(PREPARE_MATCH_SPACE(--weird-br_name))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-to-vlan --"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_port +# +TMP="$(ovs-vsctl --no-heading --columns=name list Port | tr -d '\"')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "port-to-br "], +[0], [dnl +${MATCH} +]) +# complete on ports in particular bridge. +TMP="$(ovs-vsctl list-ports br0)" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "del-port br0 "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_iface +# +for br in `ovs-vsctl list-br`; do + TMP="${TMP} $(ovs-vsctl list-ifaces $br)" +done +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "iface-to-br "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_bridge_fail_mode +# +MATCH="$(PREPARE_MATCH_SPACE(standalone secure))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-fail-mode br0 "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_key +# +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], +[0], [dnl + +]) +# since there is no key added yet, we will only get our own input. +MATCH="$(PREPARE_MATCH_SPACE(test_key))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 test_key"], +[0], [dnl +${MATCH} +]) +# now add a key, as we should see it. +ovs-vsctl br-set-external-id br0 bridge-id br0 +MATCH="$(PREPARE_MATCH_SPACE(bridge-id))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 "], +[0], [dnl +${MATCH} +]) +MATCH="$(PREPARE_MATCH_SPACE(bridge-id --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-get-external-id br0 "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_value +# +# should just return the user input. +MATCH="$(PREPARE_MATCH_SPACE(test_value --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "br-set-external-id br0 bridge-id test_value"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_column +# +TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Open_vSwitch | tr -d ':' | cut -d' ' -f1)" +UUID="$(ovs-vsctl --no-heading --columns=_uuid list Open_vSwitch | tr -d ' ')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "clear Open_vSwitch $UUID "], +[0], [dnl +${MATCH} +]) +TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | tr -d ':' | cut -d' ' -f1)" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "clear Bridge br0 "], +[0], [dnl +${MATCH} +]) +# the 'clear' command requires one or more (+) COLUMN. +# so, with one specified COLUMN 'other_config', it should still complete on +# COLUMNs, plus '--'. +MATCH="$(PREPARE_MATCH_SPACE(${TMP} --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "clear Bridge br0 other_config "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_key_value +# +# with no key available, should always get user input. +MATCH="$(PREPARE_MATCH_NOSPACE(random_key))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key"], +[0], [dnl +${MATCH} +]) +MATCH="$(PREPARE_MATCH_NOSPACE(random_key=abc))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config random_key=abc"], +[0], [dnl +${MATCH} +]) +# now add two random keys. +ovs-vsctl set Bridge br0 other_config:random_key1=abc other_config:random_val1=xyz +MATCH="$(PREPARE_MATCH_NOSPACE(random_key1= random_val1=))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add Bridge br0 other_config ran"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_column_optkey_value +# +# at first, we should complete on column. +TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Bridge | awk '/key.*value/ { print $1":"; next } { print $1; next }')" +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 "], +[0], [dnl +${MATCH} +]) +MATCH="$(PREPARE_MATCH_NOSPACE(other_config:))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other"], +[0], [dnl +${MATCH} +]) +# then, with the ':' we should complete on key. +TMP="$(ovs-vsctl --no-heading --columns=other_config list Bridge br0 | tr -d '{\"}' | tr -s ', ' '\n' | cut -d'=' -f1 | xargs printf "other_config:%s\n")" +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:"], +[0], [dnl +${MATCH} +]) +# finally, if user fill in some value, we should just complete on user input. +MATCH="$(PREPARE_MATCH_NOSPACE(other_config:random_val1=12345))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set Bridge br0 other_config:random_val1=12345"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_filename +# +touch private_key certificate +MATCH="$(PREPARE_MATCH_SPACE(private_key))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-ssl priva"], +[0], [dnl +${MATCH} +]) +MATCH="$(PREPARE_MATCH_SPACE(certificate))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-ssl private_key cer"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_target +# +MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: ssl: tcp: unix:))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager "], +[0], [dnl +${MATCH} +]) +# filename completion on unix, punix. +MATCH="$(PREPARE_MATCH_NOSPACE(unix:testsuite.log))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager unix:test"], +[0], [dnl +${MATCH} +]) +# no completion on other type, just return available types. +# in real environment, bash will not complete on anything. +MATCH="$(PREPARE_MATCH_NOSPACE(pssl: ptcp: punix: ssl: tcp: unix:))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set-manager ssl:something"], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_new +# +# test 'add-br' +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-br "], +[0], [dnl +--- BEGIN MESSAGE +Enter a new bridge: +> ovs-vsctl add-br --- END MESSAGE +]) +# user input does not change the output. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-br new-br"], +[0], [dnl +--- BEGIN MESSAGE +Enter a new bridge: +> ovs-vsctl add-br new-br--- END MESSAGE +]) +# after specifying the new bridge name, we should complete on parent bridge. +TMP="$(ovs-vsctl list-br)" +MATCH="$(PREPARE_MATCH_SPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-br new-br "], +[0], [dnl +${MATCH} +]) +# test 'add-port' +# after specifying the new port name, we should complete on the column part +# of '*COLUMN?:KEY=VALUE'. +TMP="$(ovsdb-client --no-heading list-columns Open_vSwitch Port | awk '/key.*value/ { print $1":"; next } { print $1; next }')" +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP} --))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-port br0 new-port "], +[0], [dnl +${MATCH} +]) + + +# +# test: _ovs_vsctl_complete_dashdash +# +# after '--', there should be no global options available for completion. +TMP="$(ovs-vsctl --commands | cut -d',' -f1-2 | tr -d ',[[]]' | tr -s ' ' '\n')" +MATCH="$(PREPARE_MATCH_NOSPACE(${TMP}))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "init -- "], +[0], [dnl +${MATCH} +]) +TMP="$(ovs-vsctl --no-heading --columns=name,_uuid list Port | tr -d '\"')" +MATCH="$(PREPARE_MATCH_SPACE(${TMP} newp1 newp2))" +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "add-port br0 newp1 -- add-port br1 newp2 -- set Port "], +[0], [dnl +${MATCH} +]) + +OVS_VSWITCHD_STOP +AT_CLEANUP + + +AT_SETUP([vsctl-bashcomp - negative test]) +AT_SKIP_IF([test -z ${BASH_VERSION+x}]) +OVS_VSWITCHD_START + +# complete non-matching command. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "invalid"], +[0], [dnl + +]) + +# complete after invalid command. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "invalid argu"], +[0], [dnl + +]) + +# complete non-matching end argument. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set INVALID_"], +[0], [dnl + +]) + +# complete after invalid intermediate argument. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "set INVALID_TBL "], +[1], [dnl +--- BEGIN MESSAGE +Cannot complete 'INVALID_TBL' at index 3: +> ovs-vsctl set INVALID_TBL --- END MESSAGE]) + +# complete ovs-vsctl --db=wrongdb [TAB] +# should return 1 and show nothing. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test "--db=wrongdb"], +[1], []) + +OVS_VSWITCHD_STOP +# delete ovsdb-server and try again. +AT_CHECK_UNQUOTED([ovs-vsctl-bashcomp.bash test ""], +[1], []) + AT_CLEANUP \ No newline at end of file diff --git a/utilities/automake.mk b/utilities/automake.mk index 5aae782b5..55bf44967 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -22,7 +22,9 @@ scripts_SCRIPTS += \ utilities/ovs-save scripts_DATA += utilities/ovs-lib -check_SCRIPTS += utilities/ovs-appctl-bashcomp.bash +check_SCRIPTS += \ + utilities/ovs-appctl-bashcomp.bash \ + utilities/ovs-vsctl-bashcomp.bash utilities/ovs-lib: $(top_builddir)/config.status diff --git a/utilities/ovs-command-bashcomp.INSTALL.md b/utilities/ovs-command-bashcomp.INSTALL.md index e77ead54f..411f42fe6 100644 --- a/utilities/ovs-command-bashcomp.INSTALL.md +++ b/utilities/ovs-command-bashcomp.INSTALL.md @@ -1,55 +1,79 @@ -Using bash command-line completion script ------------------------------------------ +Using bash command-line completion scripts +------------------------------------------ -ovs-command-compgen.bash adds bash command-line completion support -for ovs-appctl, ovs-dpctl, ovs-ofctl and ovsdb-tool commands. +There are two completion scripts available, ovs-appctl-bashcomp.bash +and ovs-vsctl-bashcomp.bash respectively. -Features: ---------- +ovs-appctl-bashcomp +------------------- - display available completion or complete on unfinished user input - (long option, subcommand, and argument). + ovs-appctl-bashcomp.bash adds bash command-line completion support + for ovs-appctl, ovs-dpctl, ovs-ofctl and ovsdb-tool commands. - once the subcommand (e.g. ofproto/trace) has been given, the - script will print the subcommand format. + Features: + --------- - the script can convert between keywords like 'bridge/port/interface/dp' - and the available record in ovsdb. + display available completion or complete on unfinished user input + (long option, subcommand, and argument). -Limitations: ------------- + once the subcommand (e.g. ofproto/trace) has been given, the + script will print the subcommand format. - only support small set of important keywords - (dp, datapath, bridge, switch, port, interface, iface). + the script can convert between keywords like 'bridge/port/interface/dp' + and the available record in ovsdb. - does not support parsing of nested options - (e.g. ovsdb-tool create [db [schema]]). + Limitations: + ------------ - does not support expansion on repeatitive argument - (e.g. ovs-dpctl show [dp...]). + only support small set of important keywords + (dp, datapath, bridge, switch, port, interface, iface). - only support matching on long options, and only in the format - (--option [arg], i.e. should not use --option=[arg]). + does not support parsing of nested options + (e.g. ovsdb-tool create [db [schema]]). + does not support expansion on repeatitive argument + (e.g. ovs-dpctl show [dp...]). + + only support matching on long options, and only in the format + (--option [arg], i.e. should not use --option=[arg]). + +ovs-vsctl-bashcomp +------------------- + + ovs-vsctl-bashcomp.bash adds bash command-line completion support + for ovs-vsctl command. + + Features: + --------- + + display available completion and complete on user input for + global/local options, command, and argument. + + query database and expand keywords like 'table/record/column/key' + to available completions. + + deal with argument relations like 'one and more', 'zero or one'. + + complete multiple ovs-vsctl commands cascaded via '--'. + + Limitations: + ------------ + + completion of very long ovs-vsctl command can take up to several + seconds. How to use: ----------- - To use the script, either copy it inside /etc/bash_completion.d/ - or manually run it via . ovs-command-compgen.bash. + To use the scripts, either copy them inside /etc/bash_completion.d/ or + manually run it directly in bash via . ovs-appctl-bashcomp.bash or + . ovs-vsctl-bashcomp.bash. Test: ----- - An unit testsuite is provided as ovs-command-compgen-test.bash. - To run the test, first enter ovs sandbox via: - - make sandbox && cd sandbox - - Then copy both ovs-command-compgen-test.bash and ovs-command-compgen.bash - to the current directory. Finally, run the test via: - - bash ovs-command-compgen-test.bash + Unit tests are added in tests/completion.at and integrated into autotest + framework. To run the tests, just do make check. Bug Reporting: -------------- -- 2.20.1