]> git.sur5r.net Git - i3/i3.github.io/blobdiff - docs/4.10.1/testsuite.html
save docs for 4.10.1
[i3/i3.github.io] / docs / 4.10.1 / testsuite.html
diff --git a/docs/4.10.1/testsuite.html b/docs/4.10.1/testsuite.html
new file mode 100644 (file)
index 0000000..8af4a84
--- /dev/null
@@ -0,0 +1,663 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
+    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+<link rel="icon" type="image/x-icon" href="/favicon.ico">\r
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />\r
+<meta name="generator" content="AsciiDoc 8.6.8" />\r
+<title>i3: i3 testsuite</title>\r
+<link rel="stylesheet" href="/css/style.css" type="text/css" />\r
+<link rel="stylesheet" href="/css/xhtml11.css" type="text/css" />\r
+<script type="text/javascript">\r
+/*<![CDATA[*/\r
+document.addEventListener("DOMContentLoaded", function(){asciidoc.footnotes(); asciidoc.toc(2);}, false);\r
+/*]]>*/\r
+</script>\r
+<script type="text/javascript" src="/js/asciidoc-xhtml11.js"></script>\r
+</head>\r
+<body class="article">\r
+\r
+        <div id="main">\r
+            <a href="/"><h1 id="title">i3 - improved tiling WM</h1></a>\r
+                        <ul id="nav">\r
+                                <li><a style="border-bottom: 2px solid #fff" href="/docs">Docs</a></li>\r
+                                <li><a href="/screenshots">Screens</a></li>\r
+                                <li><a href="https://faq.i3wm.org/">FAQ</a></li>\r
+                                <li><a href="/contact">Contact</a></li>\r
+                                <li><a href="https://github.com/i3/i3/issues">Bugs</a></li>\r
+                        </ul>\r
+        <br style="clear: both">\r
+<div id="content">\r
+<div id="header">\r
+<h1>i3 testsuite</h1>\r
+<span id="author">Michael Stapelberg</span><br />\r
+<span id="email"><tt>&lt;<a href="mailto:michael@i3wm.org">michael@i3wm.org</a>&gt;</tt></span><br />\r
+<span id="revdate">September 2012</span>\r
+<div id="toc">
+  <div id="toctitle">Table of Contents</div>
+  <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
+</div>\r
+</div>\r
+<div id="preamble">\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>This document explains how the i3 testsuite works, how to use it and extend it.\r
+It is targeted at developers who not necessarily have been doing testing before\r
+or have not been testing in Perl before. In general, the testsuite is not of\r
+interest for end users.</p></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_introduction">1. Introduction</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>The i3 testsuite is a collection of files which contain testcases for various\r
+i3 features. Some of them test if a certain workflow works correctly (moving\r
+windows, focus behaviour, …). Others are regression tests and contain code\r
+which previously made i3 crash or lead to unexpected behaviour. They then check\r
+if i3 still runs (meaning it did not crash) and if it handled everything\r
+correctly.</p></div>\r
+<div class="paragraph"><p>The goal of having these tests is to automatically find problems and to\r
+automatically get a feel for whether a change in the source code breaks any\r
+existing feature. After every modification of the i3 sourcecode, the developer\r
+should run the full testsuite. If one of the tests fails, the corresponding\r
+problem should be fixed (or, in some cases, the testcase has to be modified).\r
+For every bugreport, a testcase should be written to test the correct\r
+behaviour. Initially, it will fail, but after fixing the bug, it will pass.\r
+This ensures (or increases the chance) that bugs which have been fixed once\r
+will never be found again.</p></div>\r
+<div class="paragraph"><p>Also, when implementing a new feature, a testcase might be a good way to be\r
+able to easily test if the feature is working correctly. Many developers will\r
+test manually if everything works. Having a testcase not only helps you with\r
+that, but it will also be useful for every future change.</p></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_relevant_documentation">2. Relevant documentation</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>Apart from this document, you should also have a look at:</p></div>\r
+<div class="olist arabic"><ol class="arabic">\r
+<li>\r
+<p>\r
+The "Modern Perl" book, which can be found at\r
+   <a href="http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf">http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf</a>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+The latest Perl documentation of the "i3test" (general testcase setup) and\r
+   "i3test::Test" (additional test instructions) modules:\r
+   <a href="http://build.i3wm.org/docs/lib-i3test.html">http://build.i3wm.org/docs/lib-i3test.html</a> respectively\r
+   <a href="http://build.i3wm.org/docs/lib-i3test-test.html">http://build.i3wm.org/docs/lib-i3test-test.html</a>\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+The latest documentation on i3’s IPC interface:\r
+   <a href="http://build.i3wm.org/docs/ipc.html">http://build.i3wm.org/docs/ipc.html</a>\r
+</p>\r
+</li>\r
+</ol></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_implementation">3. Implementation</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>For several reasons, the i3 testsuite has been implemented in Perl:</p></div>\r
+<div class="olist arabic"><ol class="arabic">\r
+<li>\r
+<p>\r
+Perl has a long tradition of testing. Every popular/bigger Perl module which\r
+   you can find on CPAN will not only come with documentation, but also with\r
+   tests. Therefore, the available infrastructure for tests is comprehensive.\r
+   See for example the excellent <a href="http://search.cpan.org/perldoc?Test::More">http://search.cpan.org/perldoc?Test::More</a>\r
+   and the referenced <a href="http://search.cpan.org/perldoc?Test::Tutorial">http://search.cpan.org/perldoc?Test::Tutorial</a>.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+Perl is widely available and has a well-working package infrastructure.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+The author is familiar with Perl :).\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+It is a good idea to use a different language for the tests than the\r
+   implementation itself.\r
+</p>\r
+</li>\r
+</ol></div>\r
+<div class="paragraph"><p>Please do not start programming language flamewars at this point.</p></div>\r
+<div class="sect2">\r
+<h3 id="_installing_the_dependencies">3.1. Installing the dependencies</h3>\r
+<div class="paragraph"><p>As usual with Perl programs, the testsuite ships with a <tt>Makefile.PL</tt>.\r
+This file specifies which Perl modules the testsuite depends on and can be used\r
+to install all of them.</p></div>\r
+<div class="paragraph"><p>Perl modules are distributed via CPAN, and there is the official, standard CPAN\r
+client, simply called <tt>cpan</tt>. It comes with every Perl installation and can be\r
+used to install the testsuite. Many users prefer to use the more modern\r
+<tt>cpanminus</tt> instead, though (because it asks no questions and just works):</p></div>\r
+<div class="paragraph"><p>The tests additionally require <tt>Xephyr(1)</tt> to run a nested X server. Install\r
+<tt>xserver-xephyr</tt> on Debian or <tt>xorg-xserver-xephyr</tt> on Arch Linux.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Installing testsuite dependencies using cpanminus (preferred)</div>\r
+<div class="content">\r
+<pre><tt>$ cd ~/i3/testcases\r
+$ sudo apt-get install cpanminus\r
+$ sudo cpanm .</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>If you don’t want to use cpanminus for some reason, the same works with cpan:</p></div>\r
+<div class="listingblock">\r
+<div class="title">Installing testsuite dependencies using cpan</div>\r
+<div class="content">\r
+<pre><tt>$ cd ~/i3/testcases\r
+$ sudo cpan .</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>In case you don’t have root permissions, you can also install into your home\r
+directory, see <a href="http://michael.stapelberg.de/cpan/">http://michael.stapelberg.de/cpan/</a></p></div>\r
+</div>\r
+<div class="sect2">\r
+<h3 id="_mechanisms">3.2. Mechanisms</h3>\r
+<div class="sect3">\r
+<h4 id="_script_complete_run">3.2.1. Script: complete-run</h4>\r
+<div class="paragraph"><p>The testcases are run by a script called <tt>complete-run.pl</tt>. It runs all\r
+testcases by default, but you can be more specific and let it only run one or\r
+more testcases. Also, it takes care of starting up a separate instance of i3\r
+with an appropriate configuration file and creates a folder for each run\r
+containing the appropriate i3 logfile for each testcase. The latest folder can\r
+always be found under the symlink <tt>latest/</tt>. Unless told differently, it will\r
+run the tests on a separate X server instance (using Xephyr).</p></div>\r
+<div class="paragraph"><p>Xephyr will open a window where you can inspect the running test. You can run\r
+the tests without an X session with Xvfb, such as with <tt>xvfb-run\r
+./complete-run</tt>. This will also speed up the tests signficantly especially on\r
+machines without a powerful video card.</p></div>\r
+<div class="listingblock">\r
+<div class="title">Example invocation of complete-run.pl+</div>\r
+<div class="content">\r
+<pre><tt>$ cd ~/i3/testcases\r
+\r
+$ ./complete-run.pl\r
+# output omitted because it is very long\r
+All tests successful.\r
+Files=78, Tests=734, 27 wallclock secs ( 0.38 usr  0.48 sys + 17.65 cusr  3.21 csys = 21.72 CPU)\r
+Result: PASS\r
+\r
+$ ./complete-run.pl t/04-floating.t\r
+[:3] i3 startup: took 0.07s, status = 1\r
+[:3] Running t/04-floating.t with logfile testsuite-2011-09-24-16-06-04-4.0.2-226-g1eb011a/i3-log-for-04-floating.t\r
+[:3] t/04-floating.t finished\r
+[:3] killing i3\r
+output for t/04-floating.t:\r
+ok 1 - use X11::XCB::Window;\r
+ok 2 - The object isa X11::XCB::Window\r
+ok 3 - Window is mapped\r
+ok 4 - i3 raised the width to 75\r
+ok 5 - i3 raised the height to 50\r
+ok 6 - i3 did not map it to (0x0)\r
+ok 7 - The object isa X11::XCB::Window\r
+ok 8 - i3 let the width at 80\r
+ok 9 - i3 let the height at 90\r
+ok 10 - i3 mapped it to x=1\r
+ok 11 - i3 mapped it to y=18\r
+ok 12 - The object isa X11::XCB::Window\r
+ok 13 - i3 let the width at 80\r
+ok 14 - i3 let the height at 90\r
+1..14\r
+\r
+All tests successful.\r
+Files=1, Tests=14,  0 wallclock secs ( 0.01 usr  0.00 sys +  0.19 cusr  0.03 csys =  0.23 CPU)\r
+Result: PASS\r
+\r
+$ less latest/i3-log-for-04-floating.t</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>If your attempt to run the tests with a bare call to ./complete-run.pl fails, try this:</p></div>\r
+<div class="listingblock">\r
+<div class="content">\r
+<pre><tt>$ ./complete-run.pl --parallel=1 --keep-xserver-output</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This will show the output of Xephyr, which is the X server implementation we\r
+use for testing.</p></div>\r
+</div>\r
+<div class="sect3">\r
+<h4 id="_ipc_interface">3.2.2. IPC interface</h4>\r
+<div class="paragraph"><p>The testsuite makes extensive use of the IPC (Inter-Process Communication)\r
+interface which i3 provides. It is used for the startup process of i3, for\r
+terminating it cleanly and (most importantly) for modifying and getting the\r
+current state (layout tree).</p></div>\r
+<div class="paragraph"><p>See [<a href="http://i3wm.org/docs/ipc.html">http://i3wm.org/docs/ipc.html</a>] for documentation on the IPC interface.</p></div>\r
+</div>\r
+<div class="sect3">\r
+<h4 id="_x11_xcb">3.2.3. X11::XCB</h4>\r
+<div class="paragraph"><p>In order to open new windows, change attributes, get events, etc., the\r
+testsuite uses X11::XCB, a new (and quite specific to i3 at the moment) Perl\r
+module which uses the XCB protocol description to generate Perl bindings to\r
+X11. They work in a very similar way to libxcb (which i3 uses) and provide\r
+relatively high-level interfaces (objects such as <tt>X11::XCB::Window</tt>) aswell as\r
+access to the low-level interface, which is very useful when testing a window\r
+manager.</p></div>\r
+</div>\r
+</div>\r
+<div class="sect2">\r
+<h3 id="_filesystem_structure">3.3. Filesystem structure</h3>\r
+<div class="paragraph"><p>In the git root of i3, the testcases live in the folder <tt>testcases</tt>. This\r
+folder contains the <tt>complete-run.pl</tt> and a base configuration file which will\r
+be used for the tests. The different testcases (their file extension is .t, not\r
+.pl) themselves can be found in the conventionally named subfolder <tt>t</tt>:</p></div>\r
+<div class="listingblock">\r
+<div class="title">Filesystem structure</div>\r
+<div class="content">\r
+<pre><tt>├── testcases\r
+│   ├── complete-run.pl\r
+│   ├── i3-test.config\r
+│   ├── lib\r
+│   │   ├── i3test.pm\r
+│   │   ├── SocketActivation.pm\r
+│   │   └── StartXDummy.pm\r
+│   ├── t\r
+│   │   ├── 00-load.t\r
+│   │   ├── 01-tile.t\r
+│   │   ├── 02-fullscreen.t\r
+│   │   ├── ...\r
+│   │   ├── omitted for brevity\r
+│   │   ├── ...\r
+│   │   └── 74-regress-focus-toggle.t</tt></pre>\r
+</div></div>\r
+</div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_anatomy_of_a_testcase">4. Anatomy of a testcase</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>Learning by example is definitely a good strategy when you are wondering how to\r
+write a testcase. Let&#8217;s take <tt>t/11-goto.t</tt> as an easy example and go through it\r
+step by step:</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Boilerplate</div>\r
+<div class="content">\r
+<pre><tt>#!perl\r
+# vim:ts=4:sw=4:expandtab\r
+\r
+use i3test;\r
+use File::Temp;\r
+\r
+my $x = X11::XCB::Connection-&gt;new;</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This is what we call boilerplate. It exists at the top of every test file (to\r
+some extent). The first line is the shebang, which specifies that this file is\r
+a Perl script. The second line contains VIM specific settings on how to\r
+edit/format this file (use spaces instead of tabs, indent using 4 spaces).\r
+Afterwards, the <tt>i3test</tt> module is used. This module contains i3 testsuite\r
+specific functions which you are strongly encouraged to use. They make writing\r
+testcases a lot easier and will make it easier for other people to read your\r
+tests.</p></div>\r
+<div class="paragraph"><p>The next line uses the <tt>File::Temp</tt> module. This is specific to this testcase,\r
+because it needs to generate a temporary name during the test. Many testcases\r
+use only the <tt>i3test</tt> module.</p></div>\r
+<div class="paragraph"><p>The last line opens a connection to X11. You might or might not need this in\r
+your testcase, depending on whether you are going to open windows (etc.) or\r
+only use i3 commands.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Setup</div>\r
+<div class="content">\r
+<pre><tt>my $tmp = fresh_workspace;\r
+\r
+cmd 'split h';</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The first line calls i3test&#8217;s <tt>fresh_workspace</tt> function which looks for a\r
+currently unused workspace, switches to it, and returns its name. The variable\r
+<tt>$tmp</tt> will end up having a value such as <tt>"/tmp/87kBVcHbA9"</tt>. Note that this\r
+is not (necessarily) a valid path, it&#8217;s just a random workspace name.</p></div>\r
+<div class="paragraph"><p>So, now that we are on a new workspace, we ensure that the workspace uses\r
+horizontal orientation by issuing the <tt>split h</tt> command (see the i3 User&#8217;s\r
+Guide for a list of commands). This is not strictly necessary, but good style.\r
+In general, the <tt>cmd</tt> function executes the specified i3 command by using the\r
+IPC interface and returns once i3 acknowledged the command.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Setup</div>\r
+<div class="content">\r
+<pre><tt>#####################################################################\r
+# Create two windows and make sure focus switching works\r
+#####################################################################\r
+\r
+my $top = open_window($x);\r
+my $mid = open_window($x);\r
+my $bottom = open_window($x);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>In every major section of a testcase, you should put a comment like the one\r
+above. This makes it immediately clear how the file is structured.</p></div>\r
+<div class="paragraph"><p>The <tt>open_window</tt> function opens a standard window, which will then be put into\r
+tiling mode by i3. If you want a floating window, use the\r
+<tt>open_floating_window</tt> function. These functions accept the same parameters as\r
+<tt>X11::XCB::Window&#8594;new</tt>, see the i3test documentation at TODO.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Helper function</div>\r
+<div class="content">\r
+<pre><tt>#\r
+# Returns the input focus after sending the given command to i3 via IPC\r
+# and syncing with i3\r
+#\r
+sub focus_after {\r
+    my $msg = shift;\r
+\r
+    cmd $msg;\r
+    sync_with_i3 $x;\r
+    return $x-&gt;input_focus;\r
+}</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This section defines a helper function which will be used over and over in this\r
+testcase. If you have code which gets executed more than once or twice\r
+(depending on the length of your test, use your best judgement), please put it\r
+in a function. Tests should be short, concise and clear.</p></div>\r
+<div class="paragraph"><p>The <tt>focus_after</tt> function executes a command and returns the X11 focus after\r
+the command was executed. The <tt>sync_with_i3</tt> command makes sure that i3 could\r
+push its state to X11. See <a href="#i3_sync">[i3_sync]</a> to learn how this works exactly.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Test assumptions</div>\r
+<div class="content">\r
+<pre><tt>$focus = $x-&gt;input_focus;\r
+is($focus, $bottom-&gt;id, "Latest window focused");\r
+\r
+$focus = focus_after('focus left');\r
+is($focus, $mid-&gt;id, "Middle window focused");</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Now, we run the first two real tests. They use <tt>Test::More</tt>'s <tt>is</tt> function,\r
+which compares two values and prints the differences if they are not the same.\r
+After the arguments, we supply a short comment to indicate what we are testing\r
+here. This makes it vastly more easy for the developer to spot which testcase\r
+is the problem in case one fails.</p></div>\r
+<div class="paragraph"><p>The first test checks that the most recently opened window is focused.\r
+Afterwards, the command <tt>focus left</tt> is issued and it is verified that the\r
+middle window now has focus.</p></div>\r
+<div class="paragraph"><p>Note that this is not a comprehensive test of the <tt>focus</tt> command&#8201;&#8212;&#8201;we would\r
+have to test wrapping, focus when using a more complex layout, focusing the\r
+parent/child containers, etc. But that is not the point of this testcase.\r
+Instead, we just want to know if <tt>$x&#8594;input_focus</tt> corresponds with what we are\r
+expecting. If not, something is completely wrong with the test environment and\r
+this trivial test will fail.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Test that the feature does not work (yet)</div>\r
+<div class="content">\r
+<pre><tt>#####################################################################\r
+# Now goto a mark which does not exist\r
+#####################################################################\r
+\r
+my $random_mark = mktemp('mark.XXXXXX');\r
+\r
+$focus = focus_after(qq|[con_mark="$random_mark"] focus|);\r
+is($focus, $mid-&gt;id, "focus unchanged");</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Syntax hint: The qq keyword is the interpolating quote operator. It lets you\r
+chose a quote character (in this case the <tt>|</tt> character, a pipe). This makes\r
+having double quotes in our string easy.</p></div>\r
+<div class="paragraph"><p>In this new major section, a random mark (mark is an identifier for a window,\r
+see "VIM-like marks" in the i3 User’s Guide) will be generated. Afterwards, we\r
+test that trying to focus that mark will not do anything. This is important: Do\r
+not only test that using a feature has the expected outcome, but also test that\r
+using it without properly initializing it does no harm. This command could for\r
+example have changed focus anyways (a bug) or crash i3 (obviously a bug).</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Test that the feature does work</div>\r
+<div class="content">\r
+<pre><tt>cmd "mark $random_mark";\r
+\r
+$focus = focus_after('focus left');\r
+is($focus, $top-&gt;id, "Top window focused");\r
+\r
+$focus = focus_after(qq|[con_mark="$random_mark"] focus|);\r
+is($focus, $mid-&gt;id, "goto worked");</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Remember: Focus was on the middle window (we verified that earlier in "Test\r
+assumptions"). We now mark the middle window with our randomly generated mark.\r
+Afterwards, we switch focus away from the middle window to be able to tell if\r
+focusing it via its mark will work. If the test works, the goto command seems\r
+to be working.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Test corner case</div>\r
+<div class="content">\r
+<pre><tt># check that we can specify multiple criteria\r
+\r
+$focus = focus_after('focus left');\r
+is($focus, $top-&gt;id, "Top window focused");\r
+\r
+$focus = focus_after(qq|[con_mark="$random_mark" con_mark="$random_mark"] focus|);\r
+is($focus, $mid-&gt;id, "goto worked");</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>Now we test the same feature, but specifying the mark twice in the command.\r
+This should have no effect, but let’s be sure: test it and see if things go\r
+wrong.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Test second code path</div>\r
+<div class="content">\r
+<pre><tt>#####################################################################\r
+# Check whether the focus command will switch to a different\r
+# workspace if necessary\r
+#####################################################################\r
+\r
+my $tmp2 = fresh_workspace;\r
+\r
+is(focused_ws(), $tmp2, 'tmp2 now focused');\r
+\r
+cmd qq|[con_mark="$random_mark"] focus|;\r
+\r
+is(focused_ws(), $tmp, 'tmp now focused');</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>This part of the test checks that focusing windows by mark works across\r
+workspaces. It uses i3test&#8217;s <tt>focused_ws</tt> function to get the current\r
+workspace.</p></div>\r
+<div class="listingblock">\r
+<div class="title">t/11-goto.t: Test second code path</div>\r
+<div class="content">\r
+<pre><tt>done_testing;</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>The end of every testcase has to contain the <tt>done_testing</tt> line. This tells\r
+<tt>complete-run.pl</tt> that the test was finished successfully. If it does not\r
+occur, the test might have crashed during execution&#8201;&#8212;&#8201;some of the reasons why\r
+that could happen are bugs in the used modules, bugs in the testcase itself or\r
+an i3 crash resulting in the testcase being unable to communicate with i3 via\r
+IPC anymore.</p></div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="i3_sync">5. Appendix A: The i3 sync protocol</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>Consider the following situation: You open two windows in your testcase, then\r
+you use <tt>focus left</tt> and want to verify that the X11 focus has been updated\r
+properly. Sounds simple, right? Let’s assume you use this straight-forward\r
+implementation:</p></div>\r
+<div class="listingblock">\r
+<div class="title">Racey focus testcase</div>\r
+<div class="content">\r
+<pre><tt>my $left = open_window($x);\r
+my $right = open_window($x);\r
+cmd 'focus left';\r
+is($x-&gt;input_focus, $left-&gt;id, 'left window focused');</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>However, the test fails. Sometimes. Apparantly, there is a race condition in\r
+your test. If you think about it, this is because you are using two different\r
+pieces of software: You tell i3 to update focus, i3 confirms that, and then you\r
+ask X11 to give you the current focus. There is a certain time i3 needs to\r
+update the X11 state. If the testcase gets CPU time before X11 processed i3&#8217;s\r
+requests, the test will fail.</p></div>\r
+<div class="imageblock">\r
+<div class="content">\r
+<img src="i3-sync.png" alt="Diagram of the race condition" />\r
+</div>\r
+<div class="title">Figure 1. Diagram of the race condition</div>\r
+</div>\r
+<div class="paragraph"><p>One way to "solve" this would be to add <tt>sleep 0.5;</tt> after the <tt>cmd</tt> call.\r
+After 0.5 seconds it should be safe to assume that focus has been updated,\r
+right?</p></div>\r
+<div class="paragraph"><p>In practice, this usually works. However, it has several problems:</p></div>\r
+<div class="olist arabic"><ol class="arabic">\r
+<li>\r
+<p>\r
+This is obviously not a clean solution, but a workaround. Ugly.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+On very slow machines, this might not work. Unlikely, but in different\r
+   situations (a delay to wait for i3 to startup) the necessary time is much\r
+   harder to guess, even for fast machines.\r
+</p>\r
+</li>\r
+<li>\r
+<p>\r
+This <strong>wastes a lot of time</strong>. Usually, your computer is much faster than 0.5s\r
+   to update the status. However, sometimes, it might take 0.4s, so we can’t\r
+   make it <tt>sleep 0.1</tt>.\r
+</p>\r
+</li>\r
+</ol></div>\r
+<div class="paragraph"><p>To illustrate how grave the problem with wasting time actually is: Before\r
+removing all sleeps from the testsuite, a typical run using 4 separate X\r
+servers took around 50 seconds on my machine. After removing all the sleeps,\r
+we achieved times of about 25 seconds. This is very significant and influences\r
+the way you think about tests&#8201;&#8212;&#8201;the faster they are, the more likely you are\r
+to check whether everything still works quite often (which you should).</p></div>\r
+<div class="paragraph"><p>What I am trying to say is: Delays adds up quickly and make the test suite\r
+less robust.</p></div>\r
+<div class="paragraph"><p>The real solution for this problem is a mechanism which I call "the i3 sync\r
+protocol". The idea is to send a request (which does not modify state) via X11\r
+to i3 which will then be answered. Due to the request&#8217;s position in the event\r
+queue (<strong>after</strong> all previous events), you can be sure that by the time you\r
+receive the reply, all other events have been dealt with by i3 (and, more\r
+importantly, X11).</p></div>\r
+<div class="imageblock">\r
+<div class="content">\r
+<img src="i3-sync-working.png" alt="Diagram of the i3 sync solution" />\r
+</div>\r
+<div class="title">Figure 2. Diagram of the i3 sync solution</div>\r
+</div>\r
+<div class="sect2">\r
+<h3 id="_implementation_details">5.1. Implementation details</h3>\r
+<div class="paragraph"><p>The client which wants to sync with i3 initiates the protocol by sending a\r
+ClientMessage to the X11 root window:</p></div>\r
+<div class="listingblock">\r
+<div class="title">Send ClientMessage</div>\r
+<div class="content">\r
+<pre><tt># Generate a ClientMessage, see xcb_client_message_t\r
+my $msg = pack "CCSLLLLLLL",\r
+    CLIENT_MESSAGE, # response_type\r
+    32,     # format\r
+    0,      # sequence\r
+    $root,  # destination window\r
+    $x-&gt;atom(name =&gt; 'I3_SYNC')-&gt;id,\r
+\r
+    $_sync_window-&gt;id,    # data[0]: our own window id\r
+    $myrnd, # data[1]: a random value to identify the request\r
+    0,\r
+    0,\r
+    0;\r
+\r
+# Send it to the root window -- since i3 uses the SubstructureRedirect\r
+# event mask, it will get the ClientMessage.\r
+$x-&gt;send_event(0, $root, EVENT_MASK_SUBSTRUCTURE_REDIRECT, $msg);</tt></pre>\r
+</div></div>\r
+<div class="paragraph"><p>i3 will then reply with the same ClientMessage, sent to the window specified in\r
+<tt>data[0]</tt>. In the reply, <tt>data[0]</tt> and <tt>data[1]</tt> are exactly the same as in the\r
+request. You should use a random value in <tt>data[1]</tt> and check that you received\r
+the same one when getting the reply.</p></div>\r
+</div>\r
+</div>\r
+</div>\r
+<div class="sect1">\r
+<h2 id="_appendix_b_socket_activation">6. Appendix B: Socket activation</h2>\r
+<div class="sectionbody">\r
+<div class="paragraph"><p>Socket activation is a mechanism which was made popular by systemd, an init\r
+replacement. It basically describes creating a listening socket before starting\r
+a program.  systemd will invoke the program only when an actual connection to\r
+the socket is made, hence the term socket activation.</p></div>\r
+<div class="paragraph"><p>The interesting part of this (in the i3 context) is that you can very precisely\r
+detect when the program is ready (finished its initialization).</p></div>\r
+<div class="sect2">\r
+<h3 id="_preparing_the_listening_socket">6.1. Preparing the listening socket</h3>\r
+<div class="paragraph"><p><tt>complete-run.pl</tt> will create a listening UNIX socket which it will then pass\r
+to i3. This socket will be used by i3 as an additional IPC socket, just like\r
+the one it will create on its own. Passing the socket happens implicitly\r
+because children will inherit the parent’s sockets when fork()ing and sockets\r
+will continue to exist after an exec() call (unless CLOEXEC is set of course).</p></div>\r
+<div class="paragraph"><p>The only explicit things <tt>complete-run.pl</tt> has to do is setting the <tt>LISTEN_FDS</tt>\r
+environment variable to the number of sockets which exist (1 in our case) and\r
+setting the <tt>LISTEN_PID</tt> environment variable to the current process ID. Both\r
+variables are necessary so that the program (i3) knows how many sockets it\r
+should use and if the environment variable is actually intended for it. i3 will\r
+then start looking for sockets at file descriptor 3 (since 0, 1 and 2 are used\r
+for stdin, stdout and stderr, respectively).</p></div>\r
+<div class="paragraph"><p>The actual Perl code which sets up the socket, fork()s, makes sure the socket\r
+has file descriptor 3 and sets up the environment variables follows (shortened\r
+a bit):</p></div>\r
+<div class="listingblock">\r
+<div class="title">Setup socket and environment</div>\r
+<div class="content">\r
+<pre><tt>my $socket = IO::Socket::UNIX-&gt;new(\r
+    Listen =&gt; 1,\r
+    Local =&gt; $args{unix_socket_path},\r
+);\r
+\r
+my $pid = fork;\r
+if ($pid == 0) {\r
+    $ENV{LISTEN_PID} = $$;\r
+    $ENV{LISTEN_FDS} = 1;\r
+\r
+    # Only pass file descriptors 0 (stdin), 1 (stdout),\r
+    # 2 (stderr) and 3 (socket) to the child.\r
+    $^F = 3;\r
+\r
+    # If the socket does not use file descriptor 3 by chance\r
+    # already, we close fd 3 and dup2() the socket to 3.\r
+    if (fileno($socket) != 3) {\r
+        POSIX::close(3);\r
+        POSIX::dup2(fileno($socket), 3);\r
+    }\r
+\r
+    exec "/usr/bin/i3";\r
+}</tt></pre>\r
+</div></div>\r
+</div>\r
+<div class="sect2">\r
+<h3 id="_waiting_for_a_reply">6.2. Waiting for a reply</h3>\r
+<div class="paragraph"><p>In the parent process, we want to know when i3 is ready to answer our IPC\r
+requests and handle our windows. Therefore, after forking, we immediately close\r
+the listening socket (i3 will handle this side of the socket) and connect to it\r
+(remember, we are talking about a named UNIX socket) as a client. This connect\r
+call will immediately succeed because the kernel buffers it. Then, we send a\r
+request (of type GET_TREE, but that is not really relevant). Writing data to\r
+the socket will also succeed immediately because, again, the kernel buffers it\r
+(only up to a certain amount of data of course).</p></div>\r
+<div class="paragraph"><p>Afterwards, we just blockingly wait until we get an answer. In the child\r
+process, i3 will setup the listening socket in its event loop. Immediately\r
+after actually starting the event loop, it will notice a new client connecting\r
+(the parent process) and handle its request. Since all initialization has been\r
+completed successfully by the time the event loop is entered, we can now assume\r
+that i3 is ready.</p></div>\r
+</div>\r
+<div class="sect2">\r
+<h3 id="_timing_and_conclusion">6.3. Timing and conclusion</h3>\r
+<div class="paragraph"><p>A beautiful feature of this mechanism is that it does not depend on timing. It\r
+does not matter when the child process gets CPU time or when the parent process\r
+gets CPU time. On heavily loaded machines (or machines with multiple CPUs,\r
+cores or unreliable schedulers), this makes waiting for i3 much more robust.</p></div>\r
+<div class="paragraph"><p>Before using socket activation, we typically used a <tt>sleep(1)</tt> and hoped that\r
+i3 was initialized by that time. Of course, this breaks on some (slow)\r
+computers and wastes a lot of time on faster computers. By using socket\r
+activation, we decreased the total amount of time necessary to run all tests\r
+(72 files at the time of writing) from &gt; 100 seconds to 16 seconds. This makes\r
+it significantly more attractive to run the test suite more often (or at all)\r
+during development.</p></div>\r
+<div class="paragraph"><p>An alternative approach to using socket activation is polling for the existance\r
+of the IPC socket and connecting to it. While this might be slightly easier to\r
+implement, it wastes CPU time and is considerably uglier than this solution\r
+:). After all, <tt>lib/SocketActivation.pm</tt> contains only 54 SLOC.</p></div>\r
+</div>\r
+</div>\r
+</div>\r
+</div>\r
+<div id="footnotes"><hr /></div>\r
+<div id="footer" lang="de">\r
+© 2009-2011 Michael Stapelberg, <a href="/impress.html">Impressum</a>\r
+</div>\r
+</body>\r
+</html>\r