[Bash-completion-commits] [SCM] bash-completion branch, master, updated. e847d57e50ec28b793e5900c8673bd58c7393c4b

Freddy Vulto (none) freddy at rebase.
Tue Jun 9 20:54:15 UTC 2009


The following commit has been merged in the master branch:
commit e847d57e50ec28b793e5900c8673bd58c7393c4b
Author: Freddy Vulto <freddy at rebase.(none)>
Date:   Tue Jun 9 22:49:53 2009 +0200

    Improved test suite from bash-completion-lib
    Run the tests with:
    $ cd test
    $ ./runCompletionUnit
    $ ./runCompletionCompletion
    
    The last test of unit/_known_hosts gives UNRESOLVED and needs fixing.

diff --git a/bash_completion b/bash_completion
index 2c9eae4..f17d18e 100644
--- a/bash_completion
+++ b/bash_completion
@@ -1064,7 +1064,7 @@ _known_hosts()
 
 _known_hosts_real()
 {
-	local configfile
+	local configfile flag
 	local curd ocur user suffix aliases global_kh user_kh hosts i host
 	local -a kh khd config
 	local IFS=$'\n'
@@ -1199,6 +1199,8 @@ _known_hosts_real()
 	    COMPREPLY=( $( compgen -A hostname -S "$suffix" -- $cur ) )
 	fi
 
+	[ $ocur ] && cur=$ocur || unset -v cur
+
 	return 0
 }
 complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \
diff --git a/contrib/ssh b/contrib/ssh
index a445c8a..627f9b0 100644
--- a/contrib/ssh
+++ b/contrib/ssh
@@ -80,7 +80,7 @@ _ssh()
 			-N -n -q -s -T -t -V -v -X -v -Y -y -b -b -c -D -e -F \
 			-i -L -l -m -O -o -p -R -S -w' -- $cur ) )
 	else
-		if [ $COMP_CWORD -eq 1 ]; then
+		#if [ $COMP_CWORD -eq 1 ]; then
 			# Search COMP_WORDS for '-F configfile' argument
 			set -- "${COMP_WORDS[@]}"
 			while [ $# -gt 0 ]; do
@@ -96,9 +96,9 @@ _ssh()
 				shift
 			done
 			_known_hosts_real -a "$optconfigfile"
-		else
+		#else
 			COMPREPLY=( "${COMPREPLY[@]}" $( compgen -c -- $cur ) )
-		fi
+		#fi
 	fi
 
 	return 0
diff --git a/doc/bashrc b/doc/bashrc
new file mode 120000
index 0000000..e22ec0e
--- /dev/null
+++ b/doc/bashrc
@@ -0,0 +1 @@
+../test/config/bashrc
\ No newline at end of file
diff --git a/doc/html~/main.html b/doc/html~/main.html
new file mode 100644
index 0000000..adbd0c5
--- /dev/null
+++ b/doc/html~/main.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Bash-completion</title><link rel="stylesheet" href="./docbook-xsl.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /></head><body><div class="book" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 class="title"><a id="id2579299"></a>Bash-completion</h1></div><div><div class="author"><h3 class="author"><span class="firstname">Freddy</span> <span class="othername">Vulto</span> <span class="surname">(FVu)</span></h3></div></div><div><div class="revhistory"><table border="1" width="100%" summary="Revision history"><tr><th align="left" valign="top" colspan="3"><b>Revision History</b></th></tr><tr><td align="left">Revision 1.0</td><td align="left">Mar 2009</td><td align="left">FV(</td></tr></table></div></div></div><hr /></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="preface"><a href="#_preface">Preface</a></span></dt><dt><span class="part"><a href="#_coding_style_guide">I. Coding Style Guide</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_introduction">1. Introduction</a></span></dt></dl></dd><dt><span class="part"><a href="#_automated_testing">II. Automated testing</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_introduction_2">2. Introduction</a></span></dt><dt><span class="chapter"><a href="#_installing_dejagnu">3. Installing DejaGnu</a></span></dt><dd><dl><dt><span class="section"><a href="#_debian_ubuntu">3.1. Debian/Ubuntu</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_structure">4. Structure</a></span></dt><dd><dl><dt><span class="section"><a href="#_main_areas_dejagnu_tools">4.1. Main areas (DejaGnu tools)</a></span></dt><dt><span class="section"><a href="#_running_the_tests">4.2. Running the tests</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_maintenance">5. Maintenance</a></span></dt><dd><dl><dt><span class="section"><a href="#_adding_a_completion_test">5.1. Adding a completion test</a></span></dt><dt><span class="section"><a href="#_fixing_a_completion_test">5.2. Fixing a completion test</a></span></dt><dt><span class="section"><a href="#_fixing_a_unit_test">5.3. Fixing a unit test</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_rationale">6. Rationale</a></span></dt><dd><dl><dt><span class="section"><a href="#_naming_conventions">6.1. Naming conventions</a></span></dt><dd><dl><dt><span class="section"><a href="#_test_suite_or_testsuite">6.1.1. Test suite or testsuite</a></span></dt><dt><span class="section"><a href="#_script_generate">6.1.2. script/generate</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_reference">7. Reference</a></span></dt><dt><span class="chapter"><a href="#Test_context">8. Test context</a></span></dt><dd><dl><dt><span class="section"><a href="#_what_happens_when_tests_are_run">8.1. What happens when tests are run?</a></span></dt><dd><dl><dt><span class="section"><a href="#_completion">8.1.1. completion</a></span></dt><dt><span class="section"><a href="#_install">8.1.2. install</a></span></dt><dt><span class="section"><a href="#_unit">8.1.3. unit</a></span></dt></dl></dd><dt><span class="section"><a href="#_bashrc">8.2. bashrc</a></span></dt><dt><span class="section"><a href="#_inputrc">8.3. inputrc</a></span></dt></dl></dd></dl></dd><dt><span class="index"><a href="#_index">Index</a></span></dt></dl></div><div class="list-of-examples"><p><b>List of Examples</b></p><dl><dt>8.1. <a href="#id2682547">What happens when completion tests are run?</a></dt><dt>8.2. <a href="#id2682611">What happens when install tests are run?</a></dt><dt>8.3. <a href="#id2682641">What happens when unit tests are run?</a></dt></dl></div><div class="preface" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_preface"></a>Preface</h2></div></div></div><p>Bash completion extends bashs standard completion behavior to achieve
+complex command lines with just a few keystrokes.  This project was
+conceived to produce programmable completion routines for the most
+common Linux/UNIX commands, reducing the amount of typing sysadmins
+and programmers need to do on a daily basis.</p></div><div class="part" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 class="title"><a id="_coding_style_guide"></a>Part I. Coding Style Guide</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#_introduction">1. Introduction</a></span></dt></dl></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_introduction"></a>Chapter 1. Introduction</h2></div></div></div><p>This document attempts to explain the basic styles and patterns that are used in the bash completion.  New code should try to conform to these standards so that it is as easy to maintain as existing code.  Of course every rule has an exception, but it's important to know the rules nonetheless!</p><p>This is particularly directed at people new to the bash completion codebase, who are in the process of getting their code reviewed.  Before getting a review, please read over this document and make sure your code conforms to the recommendations here.</p></div></div><div class="part" lang="en" xml:lang="en"><div class="titlepage"><div><div><h1 class="title"><a id="_automated_testing"></a>Part II. Automated testing</h1></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#_introduction_2">2. Introduction</a></span></dt><dt><span class="chapter"><a href="#_installing_dejagnu">3. Installing DejaGnu</a></span></dt><dd><dl><dt><span class="section"><a href="#_debian_ubuntu">3.1. Debian/Ubuntu</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_structure">4. Structure</a></span></dt><dd><dl><dt><span class="section"><a href="#_main_areas_dejagnu_tools">4.1. Main areas (DejaGnu tools)</a></span></dt><dt><span class="section"><a href="#_running_the_tests">4.2. Running the tests</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_maintenance">5. Maintenance</a></span></dt><dd><dl><dt><span class="section"><a href="#_adding_a_completion_test">5.1. Adding a completion test</a></span></dt><dt><span class="section"><a href="#_fixing_a_completion_test">5.2. Fixing a completion test</a></span></dt><dt><span class="section"><a href="#_fixing_a_unit_test">5.3. Fixing a unit test</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_rationale">6. Rationale</a></span></dt><dd><dl><dt><span class="section"><a href="#_naming_conventions">6.1. Naming conventions</a></span></dt><dd><dl><dt><span class="section"><a href="#_test_suite_or_testsuite">6.1.1. Test suite or testsuite</a></span></dt><dt><span class="section"><a href="#_script_generate">6.1.2. script/generate</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_reference">7. Reference</a></span></dt><dt><span class="chapter"><a href="#Test_context">8. Test context</a></span></dt><dd><dl><dt><span class="section"><a href="#_what_happens_when_tests_are_run">8.1. What happens when tests are run?</a></span></dt><dd><dl><dt><span class="section"><a href="#_completion">8.1.1. completion</a></span></dt><dt><span class="section"><a href="#_install">8.1.2. install</a></span></dt><dt><span class="section"><a href="#_unit">8.1.3. unit</a></span></dt></dl></dd><dt><span class="section"><a href="#_bashrc">8.2. bashrc</a></span></dt><dt><span class="section"><a href="#_inputrc">8.3. inputrc</a></span></dt></dl></dd></dl></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_introduction_2"></a>Chapter 2. Introduction</h2></div></div></div><p>The bash-completion package contains an automated test suite.  Running the tests should help verifying that bash-completion works as expected.  The tests are also very helpful in uncovering software regressions at an early stage.</p><p>The bash-completion test suite is written on top of the <a class="ulink" href="http://www.gnu.org/software/dejagnu/" target="_top">DejaGnu</a> testing framework.  DejaGnu is written in <a class="ulink" href="http://expect.nist.gov" target="_top">Expect</a>, which in turn uses <a class="ulink" href="http://tcl.sourceforge.net" target="_top">Tcl</a> — Tool command language.</p></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_installing_dejagnu"></a>Chapter 3. Installing DejaGnu</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#_debian_ubuntu">3.1. Debian/Ubuntu</a></span></dt></dl></div><p>Installing DejaGnu should be easy using your local package manager.</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_debian_ubuntu"></a>3.1. Debian/Ubuntu</h2></div></div></div><p>On Debian/Ubuntu you can use <code class="literal">apt-get</code>:</p><pre class="screen">sudo apt-get install dejagnu</pre><p>This should also install the necessary <code class="literal">expect</code> and <code class="literal">tcl</code> packages.</p></div></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_structure"></a>Chapter 4. Structure</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#_main_areas_dejagnu_tools">4.1. Main areas (DejaGnu tools)</a></span></dt><dt><span class="section"><a href="#_running_the_tests">4.2. Running the tests</a></span></dt></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_main_areas_dejagnu_tools"></a>4.1. Main areas (DejaGnu tools)</h2></div></div></div><p>The tests are grouped into different areas, called <span class="emphasis"><em>tool</em></span> in DejaGnu:</p><div class="variablelist"><dl><dt><span class="term">
+<span class="strong"><strong>completion</strong></span>
+</span></dt><dd>
+  Functional tests per completion.
+</dd><dt><span class="term">
+<span class="strong"><strong>install</strong></span>
+</span></dt><dd>
+  Functional tests for installation and caching of the main bash-completion package.
+</dd><dt><span class="term">
+<span class="strong"><strong>unit</strong></span>
+</span></dt><dd>
+  Unit tests for bash-completion helper functions.
+</dd></dl></div><p>Each tool has a slightly different way of loading the test fixtures, see <a class="link" href="#Test_context" title="Chapter&#xA0;8.&#xA0;Test context">Test context</a> below.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_running_the_tests"></a>4.2. Running the tests</h2></div></div></div><p>The tests are run by calling <code class="literal">runtest</code> in the test directory:</p><pre class="screen">runtest --outdir log --tool completion
+runtest --outdir log --tool install
+runtest --outdir log --tool unit</pre><p>The commands above are already wrapped up in shell scripts within the <code class="literal">test</code> directory:</p><pre class="screen">./runCompletion
+./runInstall
+./runUnit</pre></div></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_maintenance"></a>Chapter 5. Maintenance</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#_adding_a_completion_test">5.1. Adding a completion test</a></span></dt><dt><span class="section"><a href="#_fixing_a_completion_test">5.2. Fixing a completion test</a></span></dt><dt><span class="section"><a href="#_fixing_a_unit_test">5.3. Fixing a unit test</a></span></dt></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_adding_a_completion_test"></a>5.1. Adding a completion test</h2></div></div></div><p>You can add script/generate to add a test.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_fixing_a_completion_test"></a>5.2. Fixing a completion test</h2></div></div></div><p>Let's consider this real-life example where an ssh completion bug is fixed.  First you're triggered by unsuccessful tests:</p><pre class="screen">$ ./runCompletion
+...
+                === completion Summary ===
+
+# of expected passes            283
+# of unexpected failures        8
+# of unresolved testcases       2
+# of unsupported tests          47</pre><p>Take a look in <code class="literal">log/completion.log</code> to find out which specific command is failing.</p><pre class="screen">$ vi log/completion.log</pre><p>Search for <code class="literal">UNRESOLVED</code> or <code class="literal">FAIL</code>.  From there scroll up to see which <code class="literal">.exp</code> test is failing:</p><pre class="screen">/@Running ./completion/ssh.exp ...
+...
+UNRESOLVED: Tab should complete ssh known-hosts at prompt</pre><p>In this case it appears <code class="literal">ssh.exp</code> is causing the problem.  Isolate the <code class="literal">ssh</code> tests by specifying just <code class="literal">ssh.exp</code> to run.  Furthermore add the <code class="literal">—debug</code> flag, so output gets logged in <code class="literal">dbg.log</code>:</p><pre class="screen">$ ./runCompletion ssh.exp --debug
+...
+                === completion Summary ===
+
+# of expected passes            1
+# of unresolved testcases       1</pre><p>Now we can have a detailed look in <code class="literal">dbg.log</code> to find out what's going wrong.  Open <code class="literal">dbg.log</code> and search for <code class="literal">UNRESOLVED</code> (or <code class="literal">FAIL</code> if that's what you're looking for):</p><pre class="screen">UNRESOLVED: Tab should complete ssh known-hosts at prompt</pre><p>From there, search up for the first line saying:</p><pre class="screen">expect: does "..." match regular expression "..."</pre><p>This tells you where the actual output differs from the expected output.  In this case it looks like the test "ssh -F fixtures/ssh/config &lt;TAB&gt;" is expecting just hostnames, whereas the actual completion is containing commands - but no hostnames.
+So what should be expected after "ssh -F fixtures/ssh/config &lt;TAB&gt;" are <span class="strong"><strong>both</strong></span> commands and hostnames.  This means both the test and the completion need fixing.  Let's start with the test.</p><pre class="screen">$ vi lib/completions/ssh.exp</pre><p>Search for the test "Tab should complete ssh known-hosts".  Here you could've seen that what was expected were hostnames ($hosts):</p><pre class="screen">set expected "^$cmd\r\n$hosts\r\n/@$cmd$"</pre><p>Adding <span class="strong"><strong>all</strong></span> commands (which could well be over 2000) to <span class="emphasis"><em>expected</em></span>, seems a bit overdone so we're gonna change things here.  Lets expect the unit test for <code class="literal">_known_hosts</code> assures all hosts are returned.  Then all we need to do here is expect one host and one command, just to be kind of sure that both hosts and commands are completed.</p><p>Looking in the fixture for ssh:</p><pre class="screen">$ vi fixtures/ssh/known_hosts</pre><p>it looks like we can add an additional host <span class="emphasis"><em>ls_known_host</em></span>.  Now if we would perform the test "ssh -F fixtures/ssh/config ls&lt;TAB&gt;" both the command <code class="literal">ls</code> and the host <code class="literal">ls_known_host</code> should come up.  Let's modify the test so:</p><pre class="screen">$ vi lib/completions/ssh.exp
+...
+set expected "^$cmd\r\n.*ls.*ls_known_host.*\r\n/@$cmd$"</pre><p>Running the test reveals we still have an unresolved test:</p><pre class="screen">$ ./runCompletion ssh.exp --debug
+...
+                === completion Summary ===
+
+# of expected passes            1
+# of unresolved testcases       1</pre><p>But if now look into the log file <code class="literal">dbg.log</code> we can see the completion only returns commands starting with <span class="emphasis"><em>ls</em></span> but fails to match our regular expression which also expects the hostname `ls_known_host':</p><pre class="screen">$ vi dbg.log
+...
+expect: does "ssh -F fixtures/ssh/config ls\r\nls           lsattr       lsb_release  lshal        lshw         lsmod        lsof         lspci        lspcmcia     lspgpot      lss16toppm\r\nlsusb\r\n/@ssh -F fixtures/ssh/config ls" (spawn_id exp9) match regular expression "^ssh -F fixtures/ssh/config ls\r\n.*ls.*ls_known_host.*\r\n/@ssh -F fixtures/ssh/config ls$"? no</pre><p>Now let's fix ssh completion:</p><pre class="screen">$ vi ../contrib/ssh
+...</pre><p>until the test shows:</p><pre class="screen">$ ./runCompletion ssh.exp
+...
+                === completion Summary ===
+
+# of expected passes            2</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_fixing_a_unit_test"></a>5.3. Fixing a unit test</h2></div></div></div><p>Now let's consider a unit test failure.  First you're triggered by unsuccessful tests:</p><pre class="screen">$ ./runUnit
+...
+                === unit Summary ===
+
+# of expected passes            1
+# of unexpected failures        1</pre><p>Take a look in <code class="literal">log/unit.log</code> to find out which specific command is failing.</p><pre class="screen">$ vi log/unit.log</pre><p>Search for <code class="literal">UNRESOLVED</code> or <code class="literal">FAIL</code>.  From there scroll up to see which <code class="literal">.exp</code> test is failing:</p><pre class="screen">/@Running ./unit/_known_hosts_real.exp ...
+...
+FAIL: Environment should stay clean</pre><p>In this case it appears <code class="literal">_known_hosts_real.exp</code> is causing the problem.  Isolate the <code class="literal">_known_hosts_real</code> test by specifying just <code class="literal">_known_hosts_real.exp</code> to run.  Furthermore add the <code class="literal">—debug</code> flag, so output gets logged in <code class="literal">dbg.log</code>:</p><pre class="screen">$ ./runUnit _known_hosts_real.exp --debug
+...
+                === completion Summary ===
+
+# of expected passes            1
+# of unexpected failures        1</pre><p>Now, if we haven't already figured out the problem, we can have a detailed look in <code class="literal">dbg.log</code> to find out what's going wrong.  Open <code class="literal">dbg.log</code> and search for <code class="literal">UNRESOLVED</code> (or <code class="literal">FAIL</code> if that's what you're looking for):</p><pre class="screen">FAIL: Environment should stay clean</pre><p>From there, search up for the first line saying:</p><pre class="screen">expect: does "..." match regular expression "..."</pre><p>This tells you where the actual output differs from the expected output.  In this case it looks like the the function <code class="literal">_known_hosts_real</code> is unexpectedly modifying global variables <code class="literal">cur</code> and <code class="literal">flag</code>.  In case you need to modify the test:</p><pre class="screen">$ vi lib/unit/_known_hosts_real.exp</pre></div></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_rationale"></a>Chapter 6. Rationale</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#_naming_conventions">6.1. Naming conventions</a></span></dt><dd><dl><dt><span class="section"><a href="#_test_suite_or_testsuite">6.1.1. Test suite or testsuite</a></span></dt><dt><span class="section"><a href="#_script_generate">6.1.2. script/generate</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_naming_conventions"></a>6.1. Naming conventions</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="_test_suite_or_testsuite"></a>6.1.1. Test suite or testsuite</h3></div></div></div><p>The primary Wikipedia page is called
+<a class="ulink" href="http://en.wikipedia.org/wiki/Test_suite" target="_top">test suite</a> and not testsuite, so that's what this
+document sticks to.</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="_script_generate"></a>6.1.2. script/generate</h3></div></div></div><p>The name and location of this code generation script come from Ruby on Rails' <a class="ulink" href="http://en.wikibooks.org/wiki/Ruby_on_Rails/Tools/Generators" target="_top">script/generate</a>.</p></div></div></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="_reference"></a>Chapter 7. Reference</h2></div></div></div><p>Within test scripts the following library functions can be used:</p></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="Test_context"></a>Chapter 8. Test context</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#_what_happens_when_tests_are_run">8.1. What happens when tests are run?</a></span></dt><dd><dl><dt><span class="section"><a href="#_completion">8.1.1. completion</a></span></dt><dt><span class="section"><a href="#_install">8.1.2. install</a></span></dt><dt><span class="section"><a href="#_unit">8.1.3. unit</a></span></dt></dl></dd><dt><span class="section"><a href="#_bashrc">8.2. bashrc</a></span></dt><dt><span class="section"><a href="#_inputrc">8.3. inputrc</a></span></dt></dl></div><p>The test environment needs to be put to fixed states when testing.  For instance the bash prompt (PS1) is set to the current test directory, followed by an ampersand (@).  The default settings for <code class="literal">bash</code> reside in <code class="literal">config/bashrc</code> and <code class="literal">config/inputrc</code>.</p><p>For each tool (completion, install, unit) a slightly different context is in effect.</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_what_happens_when_tests_are_run"></a>8.1. What happens when tests are run?</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="_completion"></a>8.1.1. completion</h3></div></div></div><p>When the completions are tested, invoking DejaGnu will result in a call to <code class="literal">completion_start()</code> which in turn will start <code class="literal">bash —rcfile config/bashrc</code>.</p><div class="example"><a id="id2682547"></a><p class="title"><b>Example 8.1. What happens when completion tests are run?</b></p><div class="example-contents"><pre class="screen">               | runtest --tool completion
+               V
+    +----------+-----------+
+    |  lib/completion.exp  |
+    |  lib/library.exp     |
+    |  config/default.exp  |
+    +----------+-----------+
+               :
+               V
+    +----------+-----------+    +---------------+    +----------------+
+    |  completion_start()  +&lt;---+ config/bashrc +&lt;---| config/inputrc |
+    | (lib/completion.exp) |    +---------------+    +----------------+
+    +----------+-----------+
+               |                                   ,+----------------------------+
+               |                               ,--+-+  "Actual completion tests" |
+               V                              /   +------------------------------+
+    +----------+-----------+    +-----------------------+
+    |   completion/*.exp   +&lt;---| lib/completions/*.exp |
+    +----------+-----------+    +-----------------------+
+               |          \                        ,+--------------------------------+
+               |           `----------------------+-+  "Completion invocation tests" |
+               V                                  +----------------------------------+
+    +----------+-----------+
+    |   completion_exit()  |
+    | (lib/completion.exp) |
+    +----------------------+</pre></div></div><br class="example-break" /><p>Setting up bash once within <code class="literal">completion_start()</code> has the speed advantage that bash - and bash-completion - need only initialize once when testing multiple completions, e.g.:</p><pre class="screen">    runtest --tool completion alias.exp cd.exp</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="_install"></a>8.1.2. install</h3></div></div></div><div class="example"><a id="id2682611"></a><p class="title"><b>Example 8.2. What happens when install tests are run?</b></p><div class="example-contents"><pre class="screen">                 | runtest --tool install
+                 V
+            +----+----+
+            | DejaGnu |
+            +----+----+
+                 |
+                 V
+    +------------+---------------+
+    | (file: config/default.exp) |
+    +------------+---------------+
+                 |
+                 V
+    +------------+------------+
+    | (file: lib/install.exp) |
+    +-------------------------+</pre></div></div><br class="example-break" /></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="_unit"></a>8.1.3. unit</h3></div></div></div><div class="example"><a id="id2682641"></a><p class="title"><b>Example 8.3. What happens when unit tests are run?</b></p><div class="example-contents"><pre class="screen">               | runtest --tool unit
+               V
+          +----+----+
+          | DejaGnu |
+          +----+----+
+               |
+               V
+    +----------+-----------+
+    |          -           |
+    | (file: lib/unit.exp) |
+    +----------------------+</pre></div></div><br class="example-break" /></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_bashrc"></a>8.2. bashrc</h2></div></div></div><p>This is the bash configuration file (bashrc) used for testing:</p><pre class="programlisting">
+# -*- mode: shell-script; sh-basic-offset: 8; indent-tabs-mode: t -*-
+# ex: ts=8 sw=8 noet filetype=sh
+#
+# bashrc file for DejaGnu testsuite
+
+        # Use emacs key bindings
+set -o emacs
+        # Use bash strict mode
+set -o posix
+        # Unset `command_not_found_handle' as defined on Debian/Ubuntu, because this
+        # troubles and slows down testing
+unset -f command_not_found_handle
+        # Set prompt to ignore current root directory; display path starting
+        # from here.  E.g. prompt: /fixtures/@
+TESTDIR=$(pwd)
+export PS1='$(wd=$(pwd); echo ${wd#$TESTDIR}/)@'
+export PS2='&gt; '
+        # Configure readline
+export INPUTRC=$TESTDIR/config/inputrc
+        # Ensure enough columns so expect doesn't have to care about line breaks
+stty columns 150
+
+. lib/library.sh
+</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_inputrc"></a>8.3. inputrc</h2></div></div></div><p>This is the readline configuration file (inputrc) used for testing:</p><pre class="programlisting">
+# -*- mode: shell-script; sh-basic-offset: 8; indent-tabs-mode: t -*-
+# ex: ts=8 sw=8 noet filetype=sh
+#
+# Readline init file for DejaGnu testsuite
+# See: info readline
+
+        # Press TAB once (instead of twice) to auto-complete
+set show-all-if-ambiguous on
+        # No bell.  No ^G in output
+set bell-style none
+        # Don't query user about viewing the number of possible completions
+set completion-query-items -1
+        # Display completions sorted horizontally, not vertically
+set print-completions-horizontally on
+        # Don't use pager when showing completions
+set page-completions off
+</pre></div></div></div><div class="index"><div class="titlepage"><div><div><h2 class="title"><a id="_index"></a>Index</h2></div></div></div><div class="index"></div></div></div></body></html>
diff --git a/doc/inputrc b/doc/inputrc
new file mode 120000
index 0000000..b6f22e6
--- /dev/null
+++ b/doc/inputrc
@@ -0,0 +1 @@
+../test/config/inputrc
\ No newline at end of file
diff --git a/doc/testing.txt b/doc/testing.txt
index 65579fd..fe41597 100644
--- a/doc/testing.txt
+++ b/doc/testing.txt
@@ -37,22 +37,23 @@ Main areas (DejaGnu tools)
 
 The tests are grouped into different areas, called _tool_ in DejaGnu:
 
-*install*:: 
-  Functional tests for installation and caching of the main bash-completion package.
 *completion*::
   Functional tests per completion.
+*install*:: 
+  Functional tests for installation and caching of the main bash-completion package.
 *unit*:: 
   Unit tests for bash-completion helper functions.
 
+Each tool has a slightly different way of loading the test fixtures, see <<Test_context,Test context>> below.
 
 Running the tests
 ~~~~~~~~~~~~~~~~~
 
 The tests are run by calling `runtest` in the test directory:
 -----------------------
-runtest --outdir=log --tool completion
-runtest --outdir=log --tool install
-runtest --outdir=log --tool unit
+runtest --outdir log --tool completion
+runtest --outdir log --tool install
+runtest --outdir log --tool unit
 -----------------------
 The commands above are already wrapped up in shell scripts within the `test` directory:
 -----------------------
@@ -74,7 +75,178 @@ Adding a completion test
 You can add script/generate to add a test.
 
 
+Fixing a completion test
+~~~~~~~~~~~~~~~~~~~~~~~~
+Let's consider this real-life example where an ssh completion bug is fixed.  First you're triggered by unsuccessful tests:
+
+----------------------------------
+$ ./runCompletion
+...
+		=== completion Summary ===
+
+# of expected passes		283
+# of unexpected failures	8
+# of unresolved testcases	2
+# of unsupported tests		47
+----------------------------------
+
+Take a look in `log/completion.log` to find out which specific command is failing.
+
+-----------------------
+$ vi log/completion.log
+-----------------------
+
+Search for `UNRESOLVED` or `FAIL`.  From there scroll up to see which `.exp` test is failing:
+
+---------------------------------------------------------
+/@Running ./completion/ssh.exp ...
+...
+UNRESOLVED: Tab should complete ssh known-hosts at prompt
+---------------------------------------------------------
+
+In this case it appears `ssh.exp` is causing the problem.  Isolate the `ssh` tests by specifying just `ssh.exp` to run.  Furthermore add the `--debug` flag, so output gets logged in `dbg.log`:
+
+----------------------------------
+$ ./runCompletion ssh.exp --debug
+...
+		=== completion Summary ===
+
+# of expected passes		1
+# of unresolved testcases	1
+----------------------------------
+
+Now we can have a detailed look in `dbg.log` to find out what's going wrong.  Open `dbg.log` and search for `UNRESOLVED` (or `FAIL` if that's what you're looking for):
+
+---------------------------------------------------------
+UNRESOLVED: Tab should complete ssh known-hosts at prompt
+---------------------------------------------------------
+
+From there, search up for the first line saying: 
+
+-------------------------------------------------
+expect: does "..." match regular expression "..."
+-------------------------------------------------
+
+This tells you where the actual output differs from the expected output.  In this case it looks like the test "ssh -F fixtures/ssh/config <TAB>" is expecting just hostnames, whereas the actual completion is containing commands - but no hostnames.
+So what should be expected after "ssh -F fixtures/ssh/config <TAB>" are *both* commands and hostnames.  This means both the test and the completion need fixing.  Let's start with the test.
+
+----------------------------
+$ vi lib/completions/ssh.exp
+----------------------------
+
+Search for the test "Tab should complete ssh known-hosts".  Here you could've seen that what was expected were hostnames ($hosts):
+
+-----------------------------------------
+set expected "^$cmd\r\n$hosts\r\n/@$cmd$"
+-----------------------------------------
+
+Adding *all* commands (which could well be over 2000) to 'expected', seems a bit overdone so we're gonna change things here.  Lets expect the unit test for `_known_hosts` assures all hosts are returned.  Then all we need to do here is expect one host and one command, just to be kind of sure that both hosts and commands are completed.
+
+Looking in the fixture for ssh:
+
+-----------------------------
+$ vi fixtures/ssh/known_hosts
+-----------------------------
+
+it looks like we can add an additional host 'ls_known_host'.  Now if we would perform the test "ssh -F fixtures/ssh/config ls<TAB>" both the command `ls` and the host `ls_known_host` should come up.  Let's modify the test so:
+
+--------------------------------------------------------
+$ vi lib/completions/ssh.exp
+...
+set expected "^$cmd\r\n.*ls.*ls_known_host.*\r\n/@$cmd$"
+--------------------------------------------------------
+
+Running the test reveals we still have an unresolved test:
+
+----------------------------------
+$ ./runCompletion ssh.exp --debug
+...
+		=== completion Summary ===
+
+# of expected passes		1
+# of unresolved testcases	1
+----------------------------------
 
+But if now look into the log file `dbg.log` we can see the completion only returns commands starting with 'ls' but fails to match our regular expression which also expects the hostname `ls_known_host':
+
+-----------------------
+$ vi dbg.log
+...
+expect: does "ssh -F fixtures/ssh/config ls\r\nls           lsattr       lsb_release  lshal        lshw         lsmod        lsof         lspci        lspcmcia     lspgpot      lss16toppm\r\nlsusb\r\n/@ssh -F fixtures/ssh/config ls" (spawn_id exp9) match regular expression "^ssh -F fixtures/ssh/config ls\r\n.*ls.*ls_known_host.*\r\n/@ssh -F fixtures/ssh/config ls$"? no
+-----------------------
+
+Now let's fix ssh completion:
+
+-------------------
+$ vi ../contrib/ssh
+...
+-------------------
+
+until the test shows:
+
+----------------------------------
+$ ./runCompletion ssh.exp
+...
+		=== completion Summary ===
+
+# of expected passes		2
+----------------------------------
+
+Fixing a unit test
+~~~~~~~~~~~~~~~~~~
+Now let's consider a unit test failure.  First you're triggered by unsuccessful tests:
+
+----------------------------------
+$ ./runUnit
+...
+		=== unit Summary ===
+
+# of expected passes		1
+# of unexpected failures	1
+----------------------------------
+
+Take a look in `log/unit.log` to find out which specific command is failing.
+
+-----------------
+$ vi log/unit.log
+-----------------
+
+Search for `UNRESOLVED` or `FAIL`.  From there scroll up to see which `.exp` test is failing:
+
+------------------------------------------
+/@Running ./unit/_known_hosts_real.exp ...
+...
+FAIL: Environment should stay clean
+------------------------------------------
+
+In this case it appears `_known_hosts_real.exp` is causing the problem.  Isolate the `_known_hosts_real` test by specifying just `_known_hosts_real.exp` to run.  Furthermore add the `--debug` flag, so output gets logged in `dbg.log`:
+
+----------------------------------
+$ ./runUnit _known_hosts_real.exp --debug
+...
+		=== completion Summary ===
+
+# of expected passes		1
+# of unexpected failures	1
+----------------------------------
+
+Now, if we haven't already figured out the problem, we can have a detailed look in `dbg.log` to find out what's going wrong.  Open `dbg.log` and search for `UNRESOLVED` (or `FAIL` if that's what you're looking for):
+
+-----------------------------------
+FAIL: Environment should stay clean
+-----------------------------------
+
+From there, search up for the first line saying: 
+
+-------------------------------------------------
+expect: does "..." match regular expression "..."
+-------------------------------------------------
+
+This tells you where the actual output differs from the expected output.  In this case it looks like the the function `_known_hosts_real` is unexpectedly modifying global variables `cur` and `flag`.  In case you need to modify the test:
+
+-----------------------------------
+$ vi lib/unit/_known_hosts_real.exp
+-----------------------------------
 
 Rationale
 ---------
@@ -100,16 +272,109 @@ The name and location of this code generation script come from Ruby on Rails' ht
 
 Within test scripts the following library functions can be used:
 
-== The test environment
-
-The tests run in a specially prepared bash environment.  The following files are used:
-* bashrch
-* inputrc
-* fixtures
+[[Test_context]]
+== Test context
+
+The test environment needs to be put to fixed states when testing.  For instance the bash prompt (PS1) is set to the current test directory, followed by an ampersand (@).  The default settings for `bash` reside in `config/bashrc` and `config/inputrc`.
+
+For each tool (completion, install, unit) a slightly different context is in effect.
+
+=== What happens when tests are run?
+
+==== completion
+
+When the completions are tested, invoking DejaGnu will result in a call to `completion_start()` which in turn will start `bash --rcfile config/bashrc`.
+
+.What happens when completion tests are run?
+----
+               | runtest --tool completion
+               V
+    +----------+-----------+
+    |  lib/completion.exp  |
+    |  lib/library.exp     |
+    |  config/default.exp  |
+    +----------+-----------+
+               :
+               V
+    +----------+-----------+    +---------------+    +----------------+
+    |  completion_start()  +<---+ config/bashrc +<---| config/inputrc |
+    | (lib/completion.exp) |    +---------------+    +----------------+
+    +----------+-----------+
+               |                                   ,+----------------------------+
+               |                               ,--+-+  "Actual completion tests" |
+               V                              /   +------------------------------+
+    +----------+-----------+    +-----------------------+
+    |   completion/*.exp   +<---| lib/completions/*.exp | 
+    +----------+-----------+    +-----------------------+ 
+               |          \                        ,+--------------------------------+
+               |           `----------------------+-+  "Completion invocation tests" |
+               V                                  +----------------------------------+
+    +----------+-----------+
+    |   completion_exit()  |
+    | (lib/completion.exp) |
+    +----------------------+
+----
+Setting up bash once within `completion_start()` has the speed advantage that bash - and bash-completion - need only initialize once when testing multiple completions, e.g.:
+----
+    runtest --tool completion alias.exp cd.exp
+----
+==== install
+
+.What happens when install tests are run?
+----
+  	         | runtest --tool install
+  	         V
+  	    +----+----+
+  	    | DejaGnu |
+  	    +----+----+
+  	         |
+  	         V
+    +------------+---------------+
+    | (file: config/default.exp) |
+    +------------+---------------+
+                 |
+                 V
+    +------------+------------+
+    | (file: lib/install.exp) |
+    +-------------------------+
+----
+
+==== unit
+
+.What happens when unit tests are run?
+----
+	       | runtest --tool unit
+	       V
+	  +----+----+
+	  | DejaGnu |
+	  +----+----+
+	       |
+	       V
+    +----------+-----------+
+    |          -           |
+    | (file: lib/unit.exp) |
+    +----------------------+
+----
 
 === bashrc
 
-Contents of bashrc:
+This is the bash configuration file (bashrc) used for testing:
+
+[source,bash]
+---------------------------------------------------------------------
+include::bashrc[]
+---------------------------------------------------------------------
+
+
+=== inputrc
+
+This is the readline configuration file (inputrc) used for testing:
+
+[source,bash]
+---------------------------------------------------------------------
+include::inputrc[]
+---------------------------------------------------------------------
+
 
 Index
 =====
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..42e178f
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1 @@
+dbg.log
diff --git a/test/completion/ssh.exp b/test/completion/ssh.exp
new file mode 100644
index 0000000..5b14ce2
--- /dev/null
+++ b/test/completion/ssh.exp
@@ -0,0 +1,6 @@
+source "lib/completions/ssh.exp"
+
+# TODO: Dynamic loading of completions.  After the tests have the first time and
+#       real completion is installed, the tests can be run a second time.
+#
+# source "lib/completions/ssh.exp"
diff --git a/test/config/bashrc b/test/config/bashrc
new file mode 100644
index 0000000..7a50f6e
--- /dev/null
+++ b/test/config/bashrc
@@ -0,0 +1,23 @@
+# -*- mode: shell-script; sh-basic-offset: 8; indent-tabs-mode: t -*-
+# ex: ts=8 sw=8 noet filetype=sh
+#
+# bashrc file for DejaGnu testsuite
+
+	# Use emacs key bindings
+set -o emacs
+	# Use bash strict mode
+set -o posix
+	# Unset `command_not_found_handle' as defined on Debian/Ubuntu, because this 
+	# troubles and slows down testing
+unset -f command_not_found_handle
+	# Set prompt to ignore current root directory; display path starting
+	# from here.  E.g. prompt: /fixtures/@
+TESTDIR=$(pwd)
+export PS1='$(wd=$(pwd); echo ${wd#$TESTDIR}/)@'
+export PS2='> '
+	# Configure readline
+export INPUTRC=$TESTDIR/config/inputrc
+	# Ensure enough columns so expect doesn't have to care about line breaks
+stty columns 150
+
+. lib/library.sh
diff --git a/test/config/default.exp b/test/config/default.exp
new file mode 100644
index 0000000..2644dbd
--- /dev/null
+++ b/test/config/default.exp
@@ -0,0 +1,11 @@
+    # Set default expect fallback routines
+expect_after {
+    eof { fail "$test at eof" }
+    timeout { fail "$test at timeout" }
+}; # expect_after()
+
+
+    # Call tool_start(), if available
+if { [info procs "${tool}_start"] != "" } {
+    ${tool}_start
+}; # if
diff --git a/test/config/inputrc b/test/config/inputrc
new file mode 100644
index 0000000..18e5a58
--- /dev/null
+++ b/test/config/inputrc
@@ -0,0 +1,16 @@
+# -*- mode: shell-script; sh-basic-offset: 8; indent-tabs-mode: t -*-
+# ex: ts=8 sw=8 noet filetype=sh
+#
+# Readline init file for DejaGnu testsuite
+# See: info readline
+
+	# Press TAB once (instead of twice) to auto-complete
+set show-all-if-ambiguous on
+	# No bell.  No ^G in output
+set bell-style none
+	# Don't query user about viewing the number of possible completions
+set completion-query-items -1
+	# Display completions sorted horizontally, not vertically
+set print-completions-horizontally on
+	# Don't use pager when showing completions
+set page-completions off
diff --git a/test/fixtures/_known_hosts/config b/test/fixtures/_known_hosts/config
new file mode 100644
index 0000000..d0a0b65
--- /dev/null
+++ b/test/fixtures/_known_hosts/config
@@ -0,0 +1,6 @@
+    UserKnownHostsFile fixtures/_known_hosts/known_hosts
+
+    # Unindented
+Host gee
+	# Indented
+    Host hus  # With comment
diff --git a/test/fixtures/_known_hosts/known_hosts b/test/fixtures/_known_hosts/known_hosts
new file mode 100644
index 0000000..d54a04d
--- /dev/null
+++ b/test/fixtures/_known_hosts/known_hosts
@@ -0,0 +1,4 @@
+|1|abc
+|1|def
+doo
+ike ssh-rsa qwerty1234/Qwerty+1234==
diff --git a/test/fixtures/_known_hosts/known_hosts2 b/test/fixtures/_known_hosts/known_hosts2
new file mode 100644
index 0000000..f719efd
--- /dev/null
+++ b/test/fixtures/_known_hosts/known_hosts2
@@ -0,0 +1 @@
+two
diff --git a/test/fixtures/_known_hosts/spaced  conf b/test/fixtures/_known_hosts/spaced  conf
new file mode 100644
index 0000000..399c7a2
--- /dev/null
+++ b/test/fixtures/_known_hosts/spaced  conf	
@@ -0,0 +1,9 @@
+
+    # Unindented
+Host gee
+    UserKnownHostsFile "fixtures/_known_hosts/spaced  known_hosts"
+
+	# Indented
+    Host hus  # With comment
+    UserKnownHostsFile "fixtures/_known_hosts/known_hosts2"
+
diff --git a/test/fixtures/_known_hosts/spaced  known_hosts b/test/fixtures/_known_hosts/spaced  known_hosts
new file mode 100644
index 0000000..d54a04d
--- /dev/null
+++ b/test/fixtures/_known_hosts/spaced  known_hosts	
@@ -0,0 +1,4 @@
+|1|abc
+|1|def
+doo
+ike ssh-rsa qwerty1234/Qwerty+1234==
diff --git a/test/fixtures/ssh/config b/test/fixtures/ssh/config
new file mode 100644
index 0000000..ab30ce4
--- /dev/null
+++ b/test/fixtures/ssh/config
@@ -0,0 +1 @@
+UserKnownHostsFile fixtures/ssh/known_hosts
diff --git a/test/fixtures/ssh/known_hosts b/test/fixtures/ssh/known_hosts
new file mode 100644
index 0000000..13b2f0f
--- /dev/null
+++ b/test/fixtures/ssh/known_hosts
@@ -0,0 +1,5 @@
+|1|abc
+|1|def
+doo
+ike ssh-rsa qwerty1234/Qwerty+1234==
+ls_known_host
diff --git a/test/lib/completion.exp b/test/lib/completion.exp
new file mode 100644
index 0000000..bcb8e76
--- /dev/null
+++ b/test/lib/completion.exp
@@ -0,0 +1,27 @@
+source lib/library.exp
+
+
+proc completion_exit {} {
+        # Exit bash
+    send "\rexit\r"
+}; # completion_exit()
+
+
+proc completion_start {} {
+    global TESTDIR spawn_id
+    set test "completion_start"
+    set TESTDIR [pwd]
+
+        # Start bash and load bash-completion
+
+    exp_spawn bash --rcfile config/bashrc
+    assert_bash_exec {} "bash --rcfile config/bashrc"
+    assert_bash_exec {BASH_COMPLETION_DIR=$(cd ..; pwd)/contrib}
+    assert_bash_exec {BASH_COMPLETION=$(cd ..; pwd)/bash_completion}
+    assert_bash_exec {source $BASH_COMPLETION}
+}; # completion_start()
+
+
+proc completion_version {} {
+    puts "bash-completion-git"
+}
diff --git a/test/lib/completions/ssh.exp b/test/lib/completions/ssh.exp
new file mode 100644
index 0000000..3794263
--- /dev/null
+++ b/test/lib/completions/ssh.exp
@@ -0,0 +1,45 @@
+proc setup {} {
+    save_env
+}; # setup()
+
+
+proc teardown {} {
+    assert_env_unmodified
+}; # teardown()
+
+
+setup
+
+
+set test "Tab should complete both commands and hostnames"
+    # Try completion
+set cmd "ssh -F fixtures/ssh/config ls"
+send "$cmd\t"
+set expected "^$cmd\r\n.*ls.*ls_known_host.*\r\n/@$cmd$"
+expect {
+    -re $expected  { pass "$test" }
+    -re /@ { unresolved "$test at prompt" }
+    default { unresolved "$test" }
+}; # expect
+
+
+sync_after_int
+
+
+set test "-F without space shouldn't error"
+    # Try completion
+set cmd "ssh -F"
+send "$cmd\t "
+set expected "^$cmd $"
+expect {
+    -re "^${cmd}bash: option requires an argument -- F" { fail "$test" }
+    -re $expected  { pass "$test" }
+    -re /@ { unresolved "$test at prompt" }
+    default { unresolved "$test" }
+}; # expect
+
+
+sync_after_int
+
+
+teardown
diff --git a/test/lib/library.exp b/test/lib/library.exp
new file mode 100644
index 0000000..55a01b3
--- /dev/null
+++ b/test/lib/library.exp
@@ -0,0 +1,264 @@
+# Execute a bash command and make sure the exit status is succesful.
+# If not, output the error message.
+# @param string $cmd  Bash command line to execute.  If emptry string (""), the
+#                     exit status of the previously executed bash command will be 
+#                     checked; specify `title' to adorn the error message.
+# @param string $title  (optional) Command title.  If empty, `cmd' is used.
+proc assert_bash_exec {{aCmd ""} {title ""}} {
+    if {[string length $aCmd] != 0} {
+        send "$aCmd\r"
+        expect -ex "$aCmd\r\n"
+    }; # if
+    if {[string length $title] == 0} {set title $aCmd}
+    expect -ex "/@"
+    set out $expect_out(buffer);  # Catch (non-expected) output
+    set cmd "echo $?"
+    send "$cmd\r"
+    expect {
+        -ex "$cmd\r\n0\r\n/@" {}
+        /@ {
+            if {[info exists multipass_name]} {
+                fail "ERROR executing bash command \"$title\""
+            }; # if
+            send_user "ERROR executing bash command \"$title\"\n$out"
+        }
+    }; # expect
+}; # assert_bash_exec()
+
+
+# Test `type ...' in bash
+# Indicate "unsupported" if `type' exits with error status.
+# @param string $command  Command to locate
+proc assert_bash_type {command} {
+    set test "$command should be available in bash"
+    set cmd "type $command &> /dev/null && echo -n 0 || echo -n 1"  
+    send "$cmd\r"
+    expect "$cmd\r\n"
+    expect {
+        -ex 0 { set result true }
+        -ex 1 { set result false; unsupported "$test" }
+    }; # expect
+    expect "/@"
+    return $result
+}; # assert_bash_type()
+
+
+# Make sure the expected items are also returned by TAB-completing the
+# specified command.
+# Break items into chunks because `expect' seems to have a limited buffer size
+# @param list $expected
+# @param string $cmd  Command given to generate items
+# @param string $test  (optional) Test titel.  Default is "$cmd<TAB> should show completions"
+# @param integer $size  (optional) Chunk size.  Default is 20.
+# @result boolean  True if successful, False if not
+proc assert_complete {expected cmd {test ""} {prompt /@} {size 20}} {
+    if {$test == ""} {set test "$cmd should show completions"}
+    send "$cmd\t"
+    expect -ex "$cmd\r\n"
+    if {[match_items $expected $test]} {
+        expect {
+            -re "$prompt$cmd$" { pass "$test" }
+            -re $prompt { unresolved "$test at prompt" }
+            -re eof { unresolved "eof" }
+        }
+    } else {
+        fail "$test"
+    }; # if
+}; # assert_complete()
+
+
+# Make sure the bash environment hasn't changed between now and the last call
+# to `save_env()'.
+# @param string $sed  Sed commands to preprocess diff output.
+#                     E.g.: s/COMP_PATH=.*/COMP_PATH=PATH/
+# @param string $file  Filename to generate environment save file from.  See
+#                      `gen_env_filename()'.
+# @param string $diff  Expected diff output (after being processed by $sed)
+# @see save_env()
+proc assert_env_unmodified {{sed ""} {file ""} {diff ""}} {
+    set test "Environment should not be modified"
+    _save_env [gen_env_filename $file 2]
+
+        # Prepare sed script
+
+        # Escape special bash characters ("\)
+    regsub -all {([\"\\])} $sed {\\\1} sed
+        # Escape newlines
+    regsub -all {\n} [string trim $sed] "\r\n" sed
+        # Mark end of sed script, so that `expect' can match on that
+    append sed "# End of sed script"
+
+        # Prepare diff script
+
+        # If diff is filled, escape newlines and make sure it ends with a newline
+    if {[string length [string trim $diff]]} {
+        regsub -all {\n} [string trim $diff] "\r\n" diff
+        append diff "\r\n"
+    } else {
+        set diff ""
+    }; # if
+
+        # Execute diff
+
+    set cmd "diff_env \"[gen_env_filename $file 1]\" \"[gen_env_filename $file 2]\" \"$sed\""
+    send "$cmd\r"
+    expect "# End of sed script\"\r\n"
+
+    expect {
+        -re "^$diff[wd]@$" { pass "$test" }
+        -re [wd]@ { 
+            fail "$test" 
+
+            # Show diff to user
+
+            set diff $expect_out(buffer)
+            # Remove possible `\r\n[wd]@' from end of diff
+            if {[string last "\r\n[wd]@" $diff] == [string length $diff] - [string length "\r\n[wd]@"]} {
+                set diff [string range $diff 0 [expr [string last "\r\n[wd]@" $diff] - 1]]
+            }; # if
+            send_user $diff;
+        }
+    }; # expect
+}; # assert_env_unmodified()
+
+
+# Make sure the specified command executed from within Tcl/Expect.
+# Fail the test with status UNSUPPORTED if Tcl fails with error "POSIX/ENOENT
+# (No such file or directory)", or UNRESOLVED if other error occurs.  
+# NOTE: Further tests are assumed if executing the command is successful.  The
+#       test isn't immediately declared to have PASSED if the command is
+#       executed successful.
+# @param string $command
+# @param string $stdout  (optional) Reference to variable to hold stdout.
+# @param string $test (optional)  Test titel
+# @see assert_bash_exec()
+proc assert_exec {cmd {stdout ''} {test ''}} {
+    if {$test == ""} {set test "$cmd should execute successful"}
+    upvar $stdout results
+    set status [catch {eval exec $cmd} results]
+    if {$status == 0} {
+        set result true
+    } else {
+        set result false
+            # Command not found (POSIX/ENOENT = no such file or directory)?
+        if {[lindex $::errorCode 0] == "POSIX" && [lindex $::errorCode 1] == "ENOENT"} {
+            # Yes, command not found;
+                # Indicate test is unsupported
+            unsupported "$test"
+        } else {
+            unresolved "$test"
+        }; # if
+    }; # if
+    return $result
+}; # assert_exec()
+
+
+# Expect items.
+# Break items into chunks because `expect' seems to have a limited buffer size
+# @param list $items
+# @param integer $size  Chunk size
+# @result boolean  True if successful, False if not
+proc match_items {items test {size 20}} {
+    set result false
+    for {set i 0} {$i < [llength $items]} {set i [expr {$i + $size}]} {
+        set expected ""
+        for {set j 0} {$j < $size && $i + $j < [llength $items]} {incr j} {
+            set item "[lindex $items [expr {$i + $j}]]"
+                # Escape special regexp characters
+            regsub -all {([\[\]\(\)\.\\\+])} $item {\\\1} item
+            set expected "${expected}$item\\s+"
+        }; # for
+        expect {
+            -re "$expected" { set result true }
+            default { set result false; break }
+            timeout { set result false; break }
+        }; # expect
+    }; # for
+    return $result
+}; # match_items()
+
+
+
+# Get real command.
+# - arg: $1  Command
+# - stdout:  Filename of command in PATH with possible symbolic links resolved.
+# - return:  Command found, empty string if not found
+proc realcommand {cmd} {
+    set result ""
+    if [string length [set path [auto_execok $cmd]]] {
+        if {[string length [auto_execok realpath]]} {
+            set result [exec realpath $path]
+        } elseif {[string length [auto_execok readlink]]} {
+            set result [exec readlink -f $path]
+        } else {
+            set result $path
+        }; # if
+    }; # if
+    return $result
+}; # realcommand()
+
+
+# Generate filename to save environment to.
+# @param string $file  File-basename to save environment to.  If the file has a
+#                      `.exp' suffix, it is removed.  E.g.:
+#                      - "file.exp" becomes "file.env1~"
+#                      - "" becomes "env.env1~"
+#                      - "filename" becomes "filename.env1~"
+#                      The file will be stored in the $TESTDIR/tmp directory.
+# @param integer $seq  Sequence number.  Must be either 1 or 2.
+proc gen_env_filename {{file ""} {seq 1}} {
+    if {[string length $file] == 0} {
+        set file "env"
+    } else {
+            # Remove possible directories
+        set file [file tail $file]
+            # Remove possible '.exp' suffix from filename
+        if {[string last ".exp" $file] == [string length $file] - [string length ".exp"]} {
+            set file [string range $file 0 [expr [string last ".exp" $file] - 1]]
+        }; # if
+    }; # if
+    return "\$TESTDIR/tmp/$file.env$seq~"
+}; # gen_env_filename()
+
+
+# Save the environment for later comparison
+# @param string $file  Filename to generate environment save file from.  See
+#                      `gen_env_filename()'.
+proc save_env {{file ""}} {
+    _save_env [gen_env_filename $file 1]
+}; # save_env()
+
+
+# Save the environment for later comparison
+# @param string  File to save the environment to.  Default is "$TESTDIR/tmp/env1~".
+# @see assert_env_unmodified()
+proc _save_env {{file ""}} {
+    assert_bash_exec "{ set; declare -F; } > $file"
+}; # save_env()
+
+
+# Interrupt completion and sync with prompt.
+# Send signals QUIT & INT.
+proc sync_after_int {} {
+    set test "Sync after INT"
+    sleep .1
+    send \031\003;  # QUIT/INT
+    expect /@
+}; # sync_after_int()
+
+
+proc sync_after_tab {} {
+        # NOTE: Wait in case completion returns nothing - because `units' isn't
+        #       installed, so that "^$cdm.*$" doesn't match too early - before
+        #       comp_install has finished
+    sleep .4
+}; # sync_after_tab()
+
+
+# Return current working directory with `TESTDIR' stripped
+# @return string  Working directory.  E.g. /, or /fixtures/
+proc wd {} {
+    global TESTDIR
+        # Remove `$TESTDIR' prefix from current working directory
+    set wd [string replace [pwd] 0 [expr [string length $TESTDIR] - 1]]/
+}
diff --git a/test/lib/library.sh b/test/lib/library.sh
new file mode 100644
index 0000000..e9b4d43
--- /dev/null
+++ b/test/lib/library.sh
@@ -0,0 +1,26 @@
+# -*- mode: shell-script; sh-basic-offset: 8; indent-tabs-mode: t -*-
+# ex: ts=8 sw=8 noet filetype=sh
+#
+# Bash library for bash-completion DejaGnu testsuite
+
+
+# Diff environment files to detect if environment is unmodified
+# @param $1  File 1
+# @param $2  File 2
+# @param $1  Additional sed script
+diff_env() {
+	diff "$1" "$2" | sed -e "
+	/^[0-9]\+[acd]/d  # Remove diff line indicators
+	/---/d            # Remove diff block separators
+	/[<>] _=/d        # Remove underscore variable
+	/[<>] PPID=/d     # Remove PPID bash variable
+	$3"
+} # diff_env()
+
+
+# Output array elements, sorted and separated by newline
+# @param $1  Name of array variable to process
+echo_array() {
+	local IFS=$'\n'
+	eval echo \"\${$1[*]}\" | sort
+}
diff --git a/test/lib/unit.exp b/test/lib/unit.exp
new file mode 100644
index 0000000..68bed0b
--- /dev/null
+++ b/test/lib/unit.exp
@@ -0,0 +1,27 @@
+source lib/library.exp
+
+
+proc unit_exit {} {
+        # Exit bash
+    send "\rexit\r"
+}; # unit_exit()
+
+
+proc unit_start {} {
+    global TESTDIR spawn_id
+    set test "unit_start"
+    set TESTDIR [pwd]
+
+        # Start bash and load bash-completion
+
+    exp_spawn bash --rcfile config/bashrc
+    assert_bash_exec {} "bash --rcfile config/bashrc"
+    assert_bash_exec {BASH_COMPLETION_DIR=$(cd ..; pwd)/contrib}
+    assert_bash_exec {BASH_COMPLETION=$(cd ..; pwd)/bash_completion}
+    assert_bash_exec {source $BASH_COMPLETION}
+}; # unit_start()
+
+
+proc unit_version {} {
+    puts "bash-completion-git"
+}
diff --git a/test/log/.gitignore b/test/log/.gitignore
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/test/log/.gitignore
@@ -0,0 +1 @@
+*
diff --git a/test/runCompletion b/test/runCompletion
new file mode 100755
index 0000000..862adea
--- /dev/null
+++ b/test/runCompletion
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# NOTE: I tried setting up bash_completion_lib within ./lib files, but DejaGnu
+#       isn't initialized at that point (i.e. output of `expect' is shown on
+#       stdout - `open_logs' hasn't run yet?).  And running code from a library
+#       file isn't probably a good idea either.
+runtest --outdir log --tool completion $* 
diff --git a/test/runInstall b/test/runInstall
new file mode 100755
index 0000000..8ee54d8
--- /dev/null
+++ b/test/runInstall
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# NOTE: I tried setting up bash_completion_lib within ./lib files, but DejaGnu
+#       isn't initialized at that point (i.e. output of `expect' is shown on
+#       stdout - `open_logs' hasn't run yet?).  And running code from a library
+#       file isn't probably a good idea either.
+runtest --outdir log --tool install $* 
diff --git a/test/runUnit b/test/runUnit
new file mode 100755
index 0000000..14e0be1
--- /dev/null
+++ b/test/runUnit
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# NOTE: I tried setting up bash_completion_lib within ./lib files, but DejaGnu
+#       isn't initialized at that point (i.e. output of `expect' is shown on
+#       stdout - `open_logs' hasn't run yet?).  And running code from a library
+#       file isn't probably a good idea either.
+runtest --outdir log --tool unit $* 
diff --git a/test/tmp/.gitignore b/test/tmp/.gitignore
new file mode 100644
index 0000000..72e8ffc
--- /dev/null
+++ b/test/tmp/.gitignore
@@ -0,0 +1 @@
+*
diff --git a/test/unit/_known_hosts.exp b/test/unit/_known_hosts.exp
new file mode 100644
index 0000000..55e254b
--- /dev/null
+++ b/test/unit/_known_hosts.exp
@@ -0,0 +1,63 @@
+proc setup {} {
+    save_env
+}; # setup()
+
+
+proc teardown {} {
+    assert_env_unmodified {/COMPREPLY=/d}
+}; # teardown()
+
+
+setup
+
+
+set test "Hosts should be put in COMPREPLY"
+
+    # Build string list of hostnames, separated by regexp whitespace marker (\s+).
+    # Example string: host1\s+host2\s+host3
+
+set hosts [exec bash -c "compgen -A hostname"]
+    # Hosts `gee' and `hus' are defined in ./fixtures/_known_hosts/config
+    # Hosts `doo' and `ike' are defined in ./fixtures/_known_hosts/known_hosts
+lappend hosts doo gee hus ike
+set hosts [lsort -ascii $hosts]
+set hosts [join $hosts "\\s+"]
+    # Call _known_hosts
+set cmd {_known_hosts -aF fixtures/_known_hosts/config; echo_array COMPREPLY}
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+    -re "^$hosts\r\n/@$"  { pass "$test" }
+    -re /@ { unresolved "$test at prompt" }
+    default { unresolved "$test" }
+}; # expect
+
+
+sync_after_int
+
+
+set test "Config file containing space should work"
+    # Build string list of hostnames, separated by regexp whitespace marker (\s+).
+    # Example string: host1\s+host2\s+host3
+set hosts [exec bash -c "compgen -A hostname"]
+    # Hosts `gee' and `hus' are defined in ./fixtures/_known_hosts/spaced  conf
+    # Hosts `doo' and `ike' are defined in ./fixtures/_known_hosts/known_hosts
+    # Host `two' is defined in ./fixtures/_known_hosts/known_hosts2
+lappend hosts gee hus
+set hosts [lsort -ascii $hosts]
+set hosts [join $hosts "\\s+"]
+    # Call _known_hosts
+set cmd {_known_hosts -F 'fixtures/_known_hosts/spaced  conf'; echo_array COMPREPLY}
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+    -re "^$hosts\r\n/@$"  { pass "$test" }
+    -re /@ { unresolved "$test at prompt" }
+    default { unresolved "$test" }
+}; # expect
+
+
+sync_after_int
+
+
+teardown

-- 
bash-completion



More information about the Bash-completion-commits mailing list