Repository: qtile/qtile

Issue:
Please allow tests to run with xvfb
In packaging qtile for Debian I have had issues in running the nose test suite as our builds run
on headless machines that do not have an X server available.

Instead of Xephyr, can you provide the option of running the tests using xvfb as an alternative?

http://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml



Discussion:
We do run our test suite on Travis, by running the Xephyr instances on Xvfb. When we were setting that up I recall there was some issue with running straight on Xvfb, but I can't think of what that was off the top of my head. If running Xephyr on Xvfb is ok, I can try to make that setup easier to run, rather than having to configure it manually as we currently do for Travis.

This sounds like a reasonable way to do it.

I've uploaded qtile to Debian with tests disabled currently, as I have run the tests locally and they have passed.

https://lists.debian.org/msgid-search/E1avmKN-00085k-0W@franck.debian.org/firsthit

(Link may not yet be active)


Resolving patch:
diff --git a/libqtile/widget/prompt.py b/libqtile/widget/prompt.py
--- a/libqtile/widget/prompt.py
+++ b/libqtile/widget/prompt.py
@@ -238,7 +238,7 @@ class CommandCompleter(object):
 
     def __init__(self, qtile, _testing=False):
         self.lookup = None
-        self.offset = None
+        self.offset = -1
         self.thisfinal = None
         self._testing = _testing
 
@@ -276,10 +276,10 @@ def complete(self, txt):
                             self.lookup.append((display, f))
                 else:
                     dirs = os.environ.get("PATH", self.DEFAULTPATH).split(":")
-                    for didx, d in enumerate(dirs):
+                    for d in dirs:
                         try:
                             d = os.path.expanduser(d)
-                            for cmd in glob.glob(os.path.join(d, "%s*" % txt)):
+                            for cmd in glob.iglob(os.path.join(d, "%s*" % txt)):
                                 if self.executable(cmd):
                                     self.lookup.append(
                                         (


Test patch:
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,434 @@
+# Copyright (c) 2011 Florian Mounier
+# Copyright (c) 2011 Anshuman Bhaduri
+# Copyright (c) 2014 Sean Vig
+# Copyright (c) 2014-2015 Tycho Andersen
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+from __future__ import print_function
+
+import libqtile
+import libqtile.ipc
+from libqtile.manager import Qtile as QtileManager
+from libqtile.log_utils import init_log
+
+import logging
+import multiprocessing
+import os
+import pytest
+import subprocess
+import sys
+import tempfile
+import time
+import traceback
+
+import xcffib
+import xcffib.xproto
+
+# the default sizes for the Xephyr windows
+WIDTH = 800
+HEIGHT = 600
+SECOND_WIDTH = 640
+SECOND_HEIGHT = 480
+
+max_sleep = 10
+sleep_time = 0.05
+
+
+def _find_display():
+    """Returns the next available display"""
+    display = 1
+    while os.path.exists("/tmp/.X%s-lock" % display):
+        display += 1
+    return display
+
+
+def whereis(program):
+    """Search PATH for executable"""
+    for path in os.environ.get('PATH', '').split(':'):
+        if os.path.exists(os.path.join(path, program)) and \
+           not os.path.isdir(os.path.join(path, program)):
+            return os.path.join(path, program)
+    return None
+
+
+class BareConfig:
+    auto_fullscreen = True
+    groups = [
+        libqtile.config.Group("a"),
+        libqtile.config.Group("b"),
+        libqtile.config.Group("c"),
+        libqtile.config.Group("d")
+    ]
+    layouts = [
+        libqtile.layout.stack.Stack(num_stacks=1),
+        libqtile.layout.stack.Stack(num_stacks=2)
+    ]
+    floating_layout = libqtile.layout.floating.Floating()
+    keys = [
+        libqtile.config.Key(
+            ["control"],
+            "k",
+            libqtile.command._Call([("layout", None)], "up")
+        ),
+        libqtile.config.Key(
+            ["control"],
+            "j",
+            libqtile.command._Call([("layout", None)], "down")
+        ),
+    ]
+    mouse = []
+    screens = [libqtile.config.Screen()]
+    main = None
+    follow_mouse_focus = False
+
+
+class Xephyr(object):
+    """Spawn Xephyr instance
+
+    Set-up a Xephyr instance with the given parameters.  The Xephyr instance
+    must be started, and then stopped.
+    """
+    def __init__(self, xinerama=True, randr=False, two_screens=True,
+                 width=WIDTH, height=HEIGHT, xoffset=None):
+        self.xinerama, self.randr = xinerama, randr
+        self.two_screens = two_screens
+
+        self.width = width
+        self.height = height
+        if xoffset is None:
+            self.xoffset = width
+        else:
+            self.xoffset = xoffset
+
+        self.proc = None  # Handle to Xephyr instance, subprocess.Popen object
+        self.display = None
+
+    def start_xephyr(self):
+        """Start Xephyr instance
+
+        Starts the Xephyr instance and sets the `self.display` to the display
+        which is used to setup the instance.
+        """
+        # we'll try twice to open Xephyr
+        for _ in range(2):
+            # get a new display
+            self.display = ":{}".format(_find_display())
+
+            # build up arguments
+            args = [
+                "Xephyr", "-name", "qtile_test",
+                self.display, "-ac",
+                "-screen", "%sx%s" % (self.width, self.height)]
+            if self.two_screens:
+                args.extend(["-origin", "%s,0" % self.xoffset, "-screen",
+                             "%sx%s" % (SECOND_WIDTH, SECOND_HEIGHT)])
+            if self.xinerama:
+                args.extend(["+xinerama"])
+            if self.randr:
+                args.extend(["+extension", "RANDR"])
+
+            self.proc = subprocess.Popen(args)
+
+            start = time.time()
+            # wait for X display to come up
+            while self.proc.poll() is None and time.time() < start + max_sleep:
+                try:
+                    conn = xcffib.connect(self.display)
+                except xcffib.ConnectionException:
+                    time.sleep(sleep_time)
+                else:
+                    conn.disconnect()
+                    return
+        else:
+            # we wern't able to get a display up
+            self.display = None
+            raise AssertionError("Unable to start Xephyr, quit with return code {:d}".format(
+                self.proc.returncode
+            ))
+
+    def stop_xephyr(self):
+        """Stop the Xephyr instance"""
+        # Xephyr must be started first
+        if self.proc is None:
+            return
+
+        # Kill xephyr only if it is running
+        if self.proc.poll() is None:
+            # We should always be able to kill xephyr nicely
+            self.proc.terminate()
+        self.proc.wait()
+
+        self.proc = None
+
+
+class Qtile(object):
+    """Spawn a Qtile instance
+
+    Setup a qtile server instance on the given display, with the given socket
+    and log files.  The qtile server must be started, and then stopped when it
+    is done.  Windows can be spawned for the qtile instance to interact with
+    with various `.test_*` methods.
+    """
+    def __init__(self, sockfile, display):
+        self.sockfile = sockfile
+        self.display = display
+
+        self.proc = None
+        self.c = None
+        self.testwindows = []
+
+    def start(self, config_class):
+        rpipe, wpipe = multiprocessing.Pipe()
+
+        def runQtile():
+            try:
+                init_log(logging.INFO, log_path=None)
+                q = QtileManager(config_class(), self.display, self.sockfile)
+                q.loop()
+            except Exception:
+                wpipe.send(traceback.format_exc())
+
+        self.proc = multiprocessing.Process(target=runQtile)
+        self.proc.start()
+
+        # First, wait for socket to appear
+        start = time.time()
+        while time.time() < start + max_sleep:
+            if os.path.exists(self.sockfile):
+                break
+
+            if rpipe.poll(sleep_time):
+                error = rpipe.recv()
+                raise AssertionError("Error launching Qtile, traceback:\n%s" % error)
+        else:
+            raise AssertionError("Error launching Qtile, socket never came up")
+
+        self.c = libqtile.command.Client(self.sockfile)
+
+        # Next, wait for server to come up
+        start = time.time()
+        while time.time() < start + max_sleep:
+            try:
+                if self.c.status() == "OK":
+                    break
+            except libqtile.ipc.IPCError:
+                pass
+
+            if rpipe.poll(sleep_time):
+                error = rpipe.recv()
+                raise AssertionError("Error launching Qtile, traceback:\n%s" % error)
+        else:
+            raise AssertionError("Error launching Qtile, quit without exception")
+
+    def create_manager(self, config_class):
+        """Create a Qtile manager instance in this thread
+
+        This should only be used when it is known that the manager will throw
+        an error and the returned manager should not be started, otherwise this
+        will likely block the thread.
+        """
+        init_log(logging.INFO, log_path=None)
+        return QtileManager(config_class(), self.display, self.sockfile)
+
+    def terminate(self):
+        if self.proc is None:
+            print("Qtile is not alive", file=sys.stderr)
+        else:
+            # try to send SIGTERM and wait up to 10 sec to quit
+            self.proc.terminate()
+            self.proc.join(10)
+
+            if self.proc.is_alive():
+                # desperate times... this probably messes with multiprocessing...
+                try:
+                    os.kill(self.proc.pid, 9)
+                    self.proc.join()
+                except OSError:
+                    # The process may have died due to some other error
+                    pass
+
+            if self.proc.exitcode:
+                print("Qtile exited with exitcode: %d" % self.proc.exitcode, file=sys.stderr)
+
+            self.proc = None
+
+        for proc in self.testwindows[:]:
+            proc.terminate()
+            proc.wait()
+
+            self.testwindows.remove(proc)
+
+    def _spawn_window(self, *args):
+        """Starts a program which opens a window
+
+        Spawns a new subprocess for a command that opens a window, given by the
+        arguments to this method.  Spawns the new process and checks that qtile
+        maps the new window.
+        """
+        if not args:
+            raise AssertionError("Trying to run nothing! (missing arguments)")
+        start = len(self.c.windows())
+
+        proc = subprocess.Popen(args, env={"DISPLAY": self.display})
+
+        while proc.poll() is None:
+            try:
+                if len(self.c.windows()) > start:
+                    break
+            except RuntimeError:
+                pass
+            time.sleep(sleep_time)
+        else:
+            proc.terminate()
+            proc.wait()
+            raise AssertionError("Window never appeared...")
+
+        self.testwindows.append(proc)
+        return proc
+
+    def kill_window(self, proc):
+        """Kill a window and check that qtile unmaps it
+
+        Kills a window created by calling one of the `self.test*` methods,
+        ensuring that qtile removes it from the `windows` attribute.
+        """
+        assert proc in self.testwindows, "Given process is not a spawned window"
+
+        start = len(self.c.windows())
+
+        proc.terminate()
+        proc.wait()
+        self.testwindows.remove(proc)
+
+        for _ in range(100):
+            if len(self.c.windows()) < start:
+                break
+            time.sleep(sleep_time)
+        else:
+            raise AssertionError("Window could not be killed...")
+
+    def testWindow(self, name):
+        python = sys.executable
+        d = os.path.dirname(os.path.realpath(__file__))
+        python = sys.executable
+        path = os.path.join(d, "scripts", "window.py")
+        return self._spawn_window(python, path, self.display, name)
+
+    def testXclock(self):
+        path = whereis("xclock")
+        return self._spawn_window(path)
+
+    def testXeyes(self):
+        path = whereis("xeyes")
+        return self._spawn_window(path)
+
+    def testGkrellm(self):
+        path = whereis("gkrellm")
+        return self._spawn_window(path)
+
+    def testXterm(self):
+        path = whereis("xterm")
+        return self._spawn_window(path)
+
+    def groupconsistency(self):
+        groups = self.c.groups()
+        screens = self.c.screens()
+        seen = set()
+        for g in groups.values():
+            scrn = g["screen"]
+            if scrn is not None:
+                if scrn in seen:
+                    raise AssertionError(
+                        "Screen referenced from more than one group.")
+                seen.add(scrn)
+                assert screens[scrn]["group"] == g["name"]
+        assert len(seen) == len(screens), "Not all screens \
+        had an attached group."
+
+
+@pytest.yield_fixture(scope="session")
+def xvfb():
+    display = ":{:d}".format(_find_display())
+    args = ["Xvfb", display, "-screen", "0", "800x600x16"]
+    proc = subprocess.Popen(args)
+
+    try:
+        # wait for X display to come up
+        start = time.time()
+        while proc.poll() is None and time.time() < start + max_sleep:
+            try:
+                conn = xcffib.connect(display)
+            except xcffib.ConnectionException:
+                time.sleep(sleep_time)
+            else:
+                conn.disconnect()
+                break
+        else:
+            raise OSError("Xvfb did not come up")
+
+        os.environ["DISPLAY"] = display
+
+        yield
+    finally:
+        proc.terminate()
+        proc.wait()
+
+
+@pytest.yield_fixture(scope="function")
+def xephyr(request, xvfb):
+    kwargs = getattr(request, "param", {})
+
+    x = Xephyr(**kwargs)
+    try:
+        x.start_xephyr()
+
+        yield x
+    finally:
+        x.stop_xephyr()
+
+
+@pytest.yield_fixture(scope="function")
+def qtile(request, xephyr):
+    config = getattr(request, "param", BareConfig)
+
+    with tempfile.NamedTemporaryFile() as f:
+        sockfile = f.name
+        q = Qtile(sockfile, xephyr.display)
+        try:
+            q.start(config)
+
+            yield q
+        finally:
+            q.terminate()
+
+
+@pytest.yield_fixture(scope="function")
+def qtile_nospawn(request, xephyr):
+    with tempfile.NamedTemporaryFile() as f:
+        sockfile = f.name
+        q = Qtile(sockfile, xephyr.display)
+
+        try:
+            yield q
+        finally:
+            q.terminate()
+
+
+no_xinerama = pytest.mark.parametrize("xephyr", [{"xinerama": False}], indirect=True)
diff --git a/test/layouts/test_floating.py b/test/layouts/test_floating.py
--- a/test/layouts/test_floating.py
+++ b/test/layouts/test_floating.py
@@ -18,11 +18,13 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
 from .layout_utils import assertFocused
+from ..conftest import no_xinerama
 
 
 class FloatingConfig(object):
@@ -41,8 +43,14 @@ class FloatingConfig(object):
     follow_mouse_focus = False
 
 
-@Xephyr(False, FloatingConfig())
-def test_float_next_prev_window(self):
+floating_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [FloatingConfig], indirect=True)(x))
+
+
+@floating_config
+def test_float_next_prev_window(qtile):
+    self = qtile
+
     # spawn three windows
     self.testWindow("one")
     self.testWindow("two")
diff --git a/test/layouts/test_matrix.py b/test/layouts/test_matrix.py
--- a/test/layouts/test_matrix.py
+++ b/test/layouts/test_matrix.py
@@ -25,10 +25,12 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
+from ..conftest import no_xinerama
 
 
 class MatrixConfig:
@@ -49,53 +51,53 @@ class MatrixConfig:
     screens = []
 
 
-@Xephyr(False, MatrixConfig())
-def test_matrix_simple(self):
-    self.testWindow("one")
-    assert self.c.layout.info()["rows"] == [["one"]]
-    self.testWindow("two")
-    assert self.c.layout.info()["rows"] == [["one", "two"]]
-    self.testWindow("three")
-    assert self.c.layout.info()["rows"] == [["one", "two"],
-                                            ["three"]]
+matrix_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [MatrixConfig], indirect=True)(x))
+
+
+@matrix_config
+def test_matrix_simple(qtile):
+    qtile.testWindow("one")
+    assert qtile.c.layout.info()["rows"] == [["one"]]
+    qtile.testWindow("two")
+    assert qtile.c.layout.info()["rows"] == [["one", "two"]]
+    qtile.testWindow("three")
+    assert qtile.c.layout.info()["rows"] == [["one", "two"], ["three"]]
 
 
-@Xephyr(False, MatrixConfig())
-def test_matrix_navigation(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
-    self.testWindow("four")
-    self.testWindow("five")
-    self.c.layout.right()
-    assert self.c.layout.info()["current_window"] == (0, 2)
-    self.c.layout.up()
-    assert self.c.layout.info()["current_window"] == (0, 1)
-    self.c.layout.up()
-    assert self.c.layout.info()["current_window"] == (0, 0)
-    self.c.layout.up()
-    assert self.c.layout.info()["current_window"] == (0, 2)
-    self.c.layout.down()
-    assert self.c.layout.info()["current_window"] == (0, 0)
-    self.c.layout.down()
-    assert self.c.layout.info()["current_window"] == (0, 1)
-    self.c.layout.right()
-    assert self.c.layout.info()["current_window"] == (1, 1)
-    self.c.layout.right()
-    assert self.c.layout.info()["current_window"] == (0, 1)
+@matrix_config
+def test_matrix_navigation(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
+    qtile.testWindow("four")
+    qtile.testWindow("five")
+    qtile.c.layout.right()
+    assert qtile.c.layout.info()["current_window"] == (0, 2)
+    qtile.c.layout.up()
+    assert qtile.c.layout.info()["current_window"] == (0, 1)
+    qtile.c.layout.up()
+    assert qtile.c.layout.info()["current_window"] == (0, 0)
+    qtile.c.layout.up()
+    assert qtile.c.layout.info()["current_window"] == (0, 2)
+    qtile.c.layout.down()
+    assert qtile.c.layout.info()["current_window"] == (0, 0)
+    qtile.c.layout.down()
+    assert qtile.c.layout.info()["current_window"] == (0, 1)
+    qtile.c.layout.right()
+    assert qtile.c.layout.info()["current_window"] == (1, 1)
+    qtile.c.layout.right()
+    assert qtile.c.layout.info()["current_window"] == (0, 1)
 
 
-@Xephyr(False, MatrixConfig())
-def test_matrix_add_remove_columns(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
-    self.testWindow("four")
-    self.testWindow("five")
-    self.c.layout.add()
-    assert self.c.layout.info()["rows"] == [["one", "two", "three"],
-                                            ["four", "five"]]
-    self.c.layout.delete()
-    assert self.c.layout.info()["rows"] == [["one", "two"],
-                                            ["three", "four"],
-                                            ["five"]]
+@matrix_config
+def test_matrix_add_remove_columns(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
+    qtile.testWindow("four")
+    qtile.testWindow("five")
+    qtile.c.layout.add()
+    assert qtile.c.layout.info()["rows"] == [["one", "two", "three"], ["four", "five"]]
+    qtile.c.layout.delete()
+    assert qtile.c.layout.info()["rows"] == [["one", "two"], ["three", "four"], ["five"]]
diff --git a/test/layouts/test_max.py b/test/layouts/test_max.py
--- a/test/layouts/test_max.py
+++ b/test/layouts/test_max.py
@@ -25,10 +25,12 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
+from ..conftest import no_xinerama
 
 
 class MaxConfig:
@@ -49,101 +51,100 @@ class MaxConfig:
     screens = []
 
 
-@Xephyr(False, MaxConfig())
-def test_max_simple(self):
-    self.testWindow("one")
-    assert self.c.layout.info()["clients"] == ["one"]
-    self.testWindow("two")
-    assert self.c.layout.info()["clients"] == ["one", "two"]
+max_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [MaxConfig], indirect=True)(x))
+
+
+@max_config
+def test_max_simple(qtile):
+    qtile.testWindow("one")
+    assert qtile.c.layout.info()["clients"] == ["one"]
+    qtile.testWindow("two")
+    assert qtile.c.layout.info()["clients"] == ["one", "two"]
 
 
-@Xephyr(False, MaxConfig())
-def test_max_updown(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
-    assert self.c.layout.info()["clients"] == ["one", "two", "three"]
-    self.c.layout.up()
-    assert self.c.groups()["a"]["focus"] == "two"
-    self.c.layout.down()
-    assert self.c.groups()["a"]["focus"] == "three"
+@max_config
+def test_max_updown(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
+    assert qtile.c.layout.info()["clients"] == ["one", "two", "three"]
+    qtile.c.layout.up()
+    assert qtile.c.groups()["a"]["focus"] == "two"
+    qtile.c.layout.down()
+    assert qtile.c.groups()["a"]["focus"] == "three"
 
 
-@Xephyr(False, MaxConfig())
-def test_max_remove(self):
-    self.testWindow("one")
-    two = self.testWindow("two")
-    assert self.c.layout.info()["clients"] == ["one", "two"]
-    self.kill(two)
-    assert self.c.layout.info()["clients"] == ["one"]
+@max_config
+def test_max_remove(qtile):
+    qtile.testWindow("one")
+    two = qtile.testWindow("two")
+    assert qtile.c.layout.info()["clients"] == ["one", "two"]
+    qtile.kill_window(two)
+    assert qtile.c.layout.info()["clients"] == ["one"]
 
 
-@Xephyr(False, MaxConfig())
-def test_closing_dialog(self):
+@max_config
+def test_closing_dialog(qtile):
     # Closing a floating window that has focus must return the focus to the
     # window that was previously focused
 
     # Start by testing a dialog that is the first open window in the group
-    dialog1 = self.testWindow("dialog1")
-    self.testWindow("one")
-    self.testWindow("two")
-    three = self.testWindow("three")
-    self.c.layout.down()
-    assert self.c.window.info()['name'] == "dialog1", self.c.window.info()[
-                                                                        'name']
-    self.c.window.toggle_floating()
-    self.kill(dialog1)
-    assert self.c.window.info()['name'] == "three", self.c.window.info()[
-                                                                        'name']
+    dialog1 = qtile.testWindow("dialog1")
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    three = qtile.testWindow("three")
+    qtile.c.layout.down()
+    assert qtile.c.window.info()['name'] == "dialog1", qtile.c.window.info()['name']
+    qtile.c.window.toggle_floating()
+    qtile.kill_window(dialog1)
+    assert qtile.c.window.info()['name'] == "three", qtile.c.window.info()['name']
 
     # Now test a dialog that is the last open window in the group
-    dialog2 = self.testWindow("dialog2")
-    self.c.window.toggle_floating()
-    self.kill(dialog2)
-    assert self.c.window.info()['name'] == "three", self.c.window.info()[
-                                                                        'name']
+    dialog2 = qtile.testWindow("dialog2")
+    qtile.c.window.toggle_floating()
+    qtile.kill_window(dialog2)
+    assert qtile.c.window.info()['name'] == "three", qtile.c.window.info()['name']
 
     # Now test a dialog that is neither the first nor the last open window in
     # the group
-    dialog3 = self.testWindow("dialog3")
-    four = self.testWindow("four")
-    self.testWindow("five")
-    self.testWindow("six")
+    dialog3 = qtile.testWindow("dialog3")
+    four = qtile.testWindow("four")
+    qtile.testWindow("five")
+    qtile.testWindow("six")
     # TODO: for a more generic test, find a way to focus 'five', then focus
     #  'dialog3' skipping 'four', so that then, after closing 'dialog3', the
     #  focus must be returned to 'five', which better represents a generic
     #  window that wasn't necessarily opened immediately after the dialog
-    self.c.layout.up()
-    self.c.layout.up()
-    self.c.layout.up()
-    assert self.c.window.info()['name'] == "dialog3", self.c.window.info()[
-                                                                        'name']
-    self.c.window.toggle_floating()
-    self.kill(dialog3)
-    assert self.c.window.info()['name'] == "four", self.c.window.info()['name']
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    assert qtile.c.window.info()['name'] == "dialog3", qtile.c.window.info()['name']
+    qtile.c.window.toggle_floating()
+    qtile.kill_window(dialog3)
+    assert qtile.c.window.info()['name'] == "four", qtile.c.window.info()['name']
 
     # Finally test a case in which the window that had focus previously is
     # closed without stealing focus from the dialog, thus requiring to find the
     # window that had focus even before that (this tests the history of focus)
-    dialog4 = self.testWindow("dialog4")
-    self.c.layout.up()
-    self.c.layout.up()
-    self.c.layout.up()
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
-    self.c.layout.down()
-    self.c.layout.down()
-    self.c.layout.down()
-    assert self.c.window.info()['name'] == "dialog4", self.c.window.info()[
-                                                                        'name']
-    self.c.window.toggle_floating()
-    self.kill(three)
-    self.kill(four)
-    self.kill(dialog4)
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
-
-
-@Xephyr(False, MaxConfig())
-def test_closing_notification(self):
+    dialog4 = qtile.testWindow("dialog4")
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
+    qtile.c.layout.down()
+    qtile.c.layout.down()
+    qtile.c.layout.down()
+    assert qtile.c.window.info()['name'] == "dialog4", qtile.c.window.info()['name']
+    qtile.c.window.toggle_floating()
+    qtile.kill_window(three)
+    qtile.kill_window(four)
+    qtile.kill_window(dialog4)
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
+
+
+@max_config
+def test_closing_notification(qtile):
     # Closing a floating window that doesn't have focus must not change the
     # currently focused window
 
@@ -152,39 +153,39 @@ def test_closing_notification(self):
 
     # Start by testing a notification that is the first open window in the
     # group
-    notification1 = self.testWindow("notification1")
-    self.c.window.toggle_floating()
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
-    self.c.layout.up()
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
-    self.kill(notification1)
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
+    notification1 = qtile.testWindow("notification1")
+    qtile.c.window.toggle_floating()
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
+    qtile.c.layout.up()
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
+    qtile.kill_window(notification1)
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
 
     # Now test a notification that is the last open window in the group
-    self.c.layout.down()
-    notification2 = self.testWindow("notification2")
-    self.c.window.toggle_floating()
-    # Create and kill 'temp', otherwise self.c.layout.up() won't work
-    temp = self.testWindow("temp")
-    self.c.layout.up()
-    self.c.layout.up()
-    self.kill(temp)
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
-    self.kill(notification2)
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
+    qtile.c.layout.down()
+    notification2 = qtile.testWindow("notification2")
+    qtile.c.window.toggle_floating()
+    # Create and kill 'temp', otherwise qtile.c.layout.up() won't work
+    temp = qtile.testWindow("temp")
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    qtile.kill_window(temp)
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
+    qtile.kill_window(notification2)
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
 
     # Now test a notification that is neither the first nor the last open
     # window in the group
-    self.c.layout.down()
-    notification3 = self.testWindow("notification3")
-    self.c.window.toggle_floating()
-    four = self.testWindow("four")
-    five = self.testWindow("five")
-    self.c.layout.up()
-    self.c.layout.up()
-    self.c.layout.up()
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
-    self.kill(notification3)
-    assert self.c.window.info()['name'] == "two", self.c.window.info()['name']
+    qtile.c.layout.down()
+    notification3 = qtile.testWindow("notification3")
+    qtile.c.window.toggle_floating()
+    four = qtile.testWindow("four")
+    five = qtile.testWindow("five")
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    qtile.c.layout.up()
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
+    qtile.kill_window(notification3)
+    assert qtile.c.window.info()['name'] == "two", qtile.c.window.info()['name']
diff --git a/test/layouts/test_ratiotile.py b/test/layouts/test_ratiotile.py
--- a/test/layouts/test_ratiotile.py
+++ b/test/layouts/test_ratiotile.py
@@ -25,11 +25,13 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+from time import sleep
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from time import sleep
-from ..utils import Xephyr
+from ..conftest import no_xinerama
 
 
 class RatioTileConfig:
@@ -44,7 +46,7 @@ class RatioTileConfig:
     layouts = [
         layout.RatioTile(ratio=.5),
         layout.RatioTile(),
-        ]
+    ]
     floating_layout = libqtile.layout.floating.Floating()
     keys = []
     mouse = []
@@ -52,63 +54,67 @@ class RatioTileConfig:
     follow_mouse_focus = False
 
 
-@Xephyr(False, RatioTileConfig())
-def test_ratiotile_add_windows(self):
+ratiotile_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [RatioTileConfig], indirect=True)(x))
+
+
+@ratiotile_config
+def test_ratiotile_add_windows(qtile):
     for i in range(12):
-        self.testWindow(str(i))
+        qtile.testWindow(str(i))
         if i == 0:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 800, 600)]
         elif i == 1:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 400, 600), (400, 0, 400, 600)]
         elif i == 2:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 266, 600), (266, 0, 266, 600), (532, 0, 268, 600)]
         elif i == 3:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 200, 600), (200, 0, 200, 600), (400, 0, 200, 600),
                 (600, 0, 200, 600)]
         elif i == 4:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 160, 600), (160, 0, 160, 600), (320, 0, 160, 600),
                 (480, 0, 160, 600), (640, 0, 160, 600)]
         elif i == 5:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 133, 600), (133, 0, 133, 600), (266, 0, 133, 600),
                 (399, 0, 133, 600), (532, 0, 133, 600), (665, 0, 135, 600)]
         elif i == 6:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 200, 300), (200, 0, 200, 300), (400, 0, 200, 300),
                 (600, 0, 200, 300), (0, 300, 266, 300),
                 (266, 300, 266, 300), (532, 300, 268, 300)]
         elif i == 7:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 200, 300), (200, 0, 200, 300), (400, 0, 200, 300),
                 (600, 0, 200, 300), (0, 300, 200, 300),
                 (200, 300, 200, 300), (400, 300, 200, 300),
                 (600, 300, 200, 300)]
         elif i == 8:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 160, 300), (160, 0, 160, 300), (320, 0, 160, 300),
                 (480, 0, 160, 300), (640, 0, 160, 300), (0, 300, 200, 300),
                 (200, 300, 200, 300), (400, 300, 200, 300),
                 (600, 300, 200, 300)]
         elif i == 9:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 160, 300), (160, 0, 160, 300), (320, 0, 160, 300),
                 (480, 0, 160, 300), (640, 0, 160, 300), (0, 300, 160, 300),
                 (160, 300, 160, 300), (320, 300, 160, 300),
                 (480, 300, 160, 300), (640, 300, 160, 300)]
         elif i == 10:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 133, 300), (133, 0, 133, 300), (266, 0, 133, 300),
                 (399, 0, 133, 300), (532, 0, 133, 300), (665, 0, 135, 300),
                 (0, 300, 160, 300), (160, 300, 160, 300),
                 (320, 300, 160, 300), (480, 300, 160, 300),
                 (640, 300, 160, 300)]
         elif i == 11:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 133, 300), (133, 0, 133, 300), (266, 0, 133, 300),
                 (399, 0, 133, 300), (532, 0, 133, 300), (665, 0, 135, 300),
                 (0, 300, 133, 300), (133, 300, 133, 300),
@@ -118,41 +124,41 @@ def test_ratiotile_add_windows(self):
             assert False
 
 
-@Xephyr(False, RatioTileConfig())
-def test_ratiotile_add_windows_golden_ratio(self):
-    self.c.next_layout()
+@ratiotile_config
+def test_ratiotile_add_windows_golden_ratio(qtile):
+    qtile.c.next_layout()
     for i in range(12):
-        self.testWindow(str(i))
+        qtile.testWindow(str(i))
         if i == 0:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 800, 600)]
         elif i == 4:
             # the rest test col order
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 400, 200), (0, 200, 400, 200), (0, 400, 400, 200),
                 (400, 0, 400, 300), (400, 300, 400, 300)]
         elif i == 5:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 400, 200), (0, 200, 400, 200), (0, 400, 400, 200),
                 (400, 0, 400, 200), (400, 200, 400, 200),
                 (400, 400, 400, 200)]
 
         elif i == 9:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 266, 150), (0, 150, 266, 150), (0, 300, 266, 150),
                 (0, 450, 266, 150), (266, 0, 266, 150),
                 (266, 150, 266, 150), (266, 300, 266, 150),
                 (266, 450, 266, 150), (532, 0, 266, 300),
                 (532, 300, 266, 300)]
         elif i == 10:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 266, 150), (0, 150, 266, 150), (0, 300, 266, 150),
                 (0, 450, 266, 150), (266, 0, 266, 150),
                 (266, 150, 266, 150), (266, 300, 266, 150),
                 (266, 450, 266, 150), (532, 0, 266, 200),
                 (532, 200, 266, 200), (532, 400, 266, 200)]
         elif i == 11:
-            assert self.c.layout.info()['layout_info'] == [
+            assert qtile.c.layout.info()['layout_info'] == [
                 (0, 0, 266, 150), (0, 150, 266, 150), (0, 300, 266, 150),
                 (0, 450, 266, 150), (266, 0, 266, 150),
                 (266, 150, 266, 150), (266, 300, 266, 150),
@@ -161,28 +167,28 @@ def test_ratiotile_add_windows_golden_ratio(self):
                 (532, 450, 266, 150)]
 
 
-@Xephyr(False, RatioTileConfig())
-def test_ratiotile_basic(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
+@ratiotile_config
+def test_ratiotile_basic(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
     sleep(0.1)
-    assert self.c.window.info()['width'] == 264
-    assert self.c.window.info()['height'] == 598
-    assert self.c.window.info()['x'] == 0
-    assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['name'] == 'three'
+    assert qtile.c.window.info()['width'] == 264
+    assert qtile.c.window.info()['height'] == 598
+    assert qtile.c.window.info()['x'] == 0
+    assert qtile.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['name'] == 'three'
 
-    self.c.group.next_window()
-    assert self.c.window.info()['width'] == 264
-    assert self.c.window.info()['height'] == 598
-    assert self.c.window.info()['x'] == 266
-    assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['name'] == 'two'
+    qtile.c.group.next_window()
+    assert qtile.c.window.info()['width'] == 264
+    assert qtile.c.window.info()['height'] == 598
+    assert qtile.c.window.info()['x'] == 266
+    assert qtile.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['name'] == 'two'
 
-    self.c.group.next_window()
-    assert self.c.window.info()['width'] == 266
-    assert self.c.window.info()['height'] == 598
-    assert self.c.window.info()['x'] == 532
-    assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['name'] == 'one'
+    qtile.c.group.next_window()
+    assert qtile.c.window.info()['width'] == 266
+    assert qtile.c.window.info()['height'] == 598
+    assert qtile.c.window.info()['x'] == 532
+    assert qtile.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['name'] == 'one'
diff --git a/test/layouts/test_slice.py b/test/layouts/test_slice.py
--- a/test/layouts/test_slice.py
+++ b/test/layouts/test_slice.py
@@ -25,11 +25,13 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
 from .layout_utils import assertDimensions, assertFocused, assertFocusPath
+from ..conftest import no_xinerama
 
 
 class SliceConfig:
@@ -40,14 +42,14 @@ class SliceConfig:
     ]
     layouts = [
         layout.Slice(side='left', width=200, wname='slice',
-            fallback=layout.Stack(num_stacks=1, border_width=0)),
+                     fallback=layout.Stack(num_stacks=1, border_width=0)),
         layout.Slice(side='right', width=200, wname='slice',
-            fallback=layout.Stack(num_stacks=1, border_width=0)),
+                     fallback=layout.Stack(num_stacks=1, border_width=0)),
         layout.Slice(side='top', width=200, wname='slice',
-            fallback=layout.Stack(num_stacks=1, border_width=0)),
+                     fallback=layout.Stack(num_stacks=1, border_width=0)),
         layout.Slice(side='bottom', width=200, wname='slice',
-            fallback=layout.Stack(num_stacks=1, border_width=0)),
-        ]
+                     fallback=layout.Stack(num_stacks=1, border_width=0)),
+    ]
     floating_layout = libqtile.layout.floating.Floating()
     keys = []
     mouse = []
@@ -55,77 +57,81 @@ class SliceConfig:
     follow_mouse_focus = False
 
 
-@Xephyr(False, SliceConfig())
-def test_no_slice(self):
-    self.testWindow('one')
-    assertDimensions(self, 200, 0, 600, 600)
-    self.testWindow('two')
-    assertDimensions(self, 200, 0, 600, 600)
-
-
-@Xephyr(False, SliceConfig())
-def test_slice_first(self):
-    self.testWindow('slice')
-    assertDimensions(self, 0, 0, 200, 600)
-    self.testWindow('two')
-    assertDimensions(self, 200, 0, 600, 600)
-
-
-@Xephyr(False, SliceConfig())
-def test_slice_last(self):
-    self.testWindow('one')
-    assertDimensions(self, 200, 0, 600, 600)
-    self.testWindow('slice')
-    assertDimensions(self, 0, 0, 200, 600)
-
-
-@Xephyr(False, SliceConfig())
-def test_slice_focus(self):
-    one = self.testWindow('one')
-    assertFocused(self, 'one')
-    two = self.testWindow('two')
-    assertFocused(self, 'two')
-    slice = self.testWindow('slice')
-    assertFocused(self, 'slice')
-    assertFocusPath(self, 'slice')
-    three = self.testWindow('three')
-    assertFocusPath(self, 'slice', 'three')
-    self.kill(two)
-    assertFocusPath(self, 'slice', 'one')
-    self.kill(slice)
-    assertFocusPath(self, 'one')
-    slice = self.testWindow('slice')
-    assertFocusPath(self, 'one', 'slice')
-
-
-@Xephyr(False, SliceConfig())
-def test_all_slices(self):
-    self.testWindow('slice')  # left
-    assertDimensions(self, 0, 0, 200, 600)
-    self.c.next_layout()  # right
-    assertDimensions(self, 600, 0, 200, 600)
-    self.c.next_layout()  # top
-    assertDimensions(self, 0, 0, 800, 200)
-    self.c.next_layout()  # bottom
-    assertDimensions(self, 0, 400, 800, 200)
-    self.c.next_layout()  # left again
-    self.testWindow('one')
-    assertDimensions(self, 200, 0, 600, 600)
-    self.c.next_layout()  # right
-    assertDimensions(self, 0, 0, 600, 600)
-    self.c.next_layout()  # top
-    assertDimensions(self, 0, 200, 800, 400)
-    self.c.next_layout()  # bottom
-    assertDimensions(self, 0, 0, 800, 400)
-
-
-@Xephyr(False, SliceConfig())
-def test_command_propagation(self):
-    self.testWindow('slice')
-    self.testWindow('one')
-    self.testWindow('two')
-    info = self.c.layout.info()
+slice_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [SliceConfig], indirect=True)(x))
+
+
+@slice_config
+def test_no_slice(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 200, 0, 600, 600)
+    qtile.testWindow('two')
+    assertDimensions(qtile, 200, 0, 600, 600)
+
+
+@slice_config
+def test_slice_first(qtile):
+    qtile.testWindow('slice')
+    assertDimensions(qtile, 0, 0, 200, 600)
+    qtile.testWindow('two')
+    assertDimensions(qtile, 200, 0, 600, 600)
+
+
+@slice_config
+def test_slice_last(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 200, 0, 600, 600)
+    qtile.testWindow('slice')
+    assertDimensions(qtile, 0, 0, 200, 600)
+
+
+@slice_config
+def test_slice_focus(qtile):
+    one = qtile.testWindow('one')
+    assertFocused(qtile, 'one')
+    two = qtile.testWindow('two')
+    assertFocused(qtile, 'two')
+    slice = qtile.testWindow('slice')
+    assertFocused(qtile, 'slice')
+    assertFocusPath(qtile, 'slice')
+    three = qtile.testWindow('three')
+    assertFocusPath(qtile, 'slice', 'three')
+    qtile.kill_window(two)
+    assertFocusPath(qtile, 'slice', 'one')
+    qtile.kill_window(slice)
+    assertFocusPath(qtile, 'one')
+    slice = qtile.testWindow('slice')
+    assertFocusPath(qtile, 'one', 'slice')
+
+
+@slice_config
+def test_all_slices(qtile):
+    qtile.testWindow('slice')  # left
+    assertDimensions(qtile, 0, 0, 200, 600)
+    qtile.c.next_layout()  # right
+    assertDimensions(qtile, 600, 0, 200, 600)
+    qtile.c.next_layout()  # top
+    assertDimensions(qtile, 0, 0, 800, 200)
+    qtile.c.next_layout()  # bottom
+    assertDimensions(qtile, 0, 400, 800, 200)
+    qtile.c.next_layout()  # left again
+    qtile.testWindow('one')
+    assertDimensions(qtile, 200, 0, 600, 600)
+    qtile.c.next_layout()  # right
+    assertDimensions(qtile, 0, 0, 600, 600)
+    qtile.c.next_layout()  # top
+    assertDimensions(qtile, 0, 200, 800, 400)
+    qtile.c.next_layout()  # bottom
+    assertDimensions(qtile, 0, 0, 800, 400)
+
+
+@slice_config
+def test_command_propagation(qtile):
+    qtile.testWindow('slice')
+    qtile.testWindow('one')
+    qtile.testWindow('two')
+    info = qtile.c.layout.info()
     assert info['name'] == 'slice', info['name']
-    org_height = self.c.window.info()['height']
-    self.c.layout.toggle_split()
-    assert self.c.window.info()['height'] != org_height
+    org_height = qtile.c.window.info()['height']
+    qtile.c.layout.toggle_split()
+    assert qtile.c.window.info()['height'] != org_height
diff --git a/test/layouts/test_stack.py b/test/layouts/test_stack.py
--- a/test/layouts/test_stack.py
+++ b/test/layouts/test_stack.py
@@ -25,10 +25,12 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
+from ..conftest import no_xinerama
 
 
 class StackConfig:
@@ -51,6 +53,10 @@ class StackConfig:
     follow_mouse_focus = False
 
 
+stack_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [StackConfig], indirect=True)(x))
+
+
 def _stacks(self):
     stacks = []
     for i in self.c.layout.info()["stacks"]:
@@ -60,167 +66,167 @@ def _stacks(self):
     return stacks
 
 
-@Xephyr(False, StackConfig())
-def test_stack_commands(self):
-    assert self.c.layout.info()["current_stack"] == 0
-    self.testWindow("one")
-    assert _stacks(self) == [["one"], []]
-    assert self.c.layout.info()["current_stack"] == 0
-    self.testWindow("two")
-    assert _stacks(self) == [["one"], ["two"]]
-    assert self.c.layout.info()["current_stack"] == 1
-    self.testWindow("three")
-    assert _stacks(self) == [["one"], ["three", "two"]]
-    assert self.c.layout.info()["current_stack"] == 1
-
-    self.c.layout.delete()
-    assert _stacks(self) == [["one", "three", "two"]]
-    info = self.c.groups()["a"]
+@stack_config
+def test_stack_commands(qtile):
+    assert qtile.c.layout.info()["current_stack"] == 0
+    qtile.testWindow("one")
+    assert _stacks(qtile) == [["one"], []]
+    assert qtile.c.layout.info()["current_stack"] == 0
+    qtile.testWindow("two")
+    assert _stacks(qtile) == [["one"], ["two"]]
+    assert qtile.c.layout.info()["current_stack"] == 1
+    qtile.testWindow("three")
+    assert _stacks(qtile) == [["one"], ["three", "two"]]
+    assert qtile.c.layout.info()["current_stack"] == 1
+
+    qtile.c.layout.delete()
+    assert _stacks(qtile) == [["one", "three", "two"]]
+    info = qtile.c.groups()["a"]
     assert info["focus"] == "one"
-    self.c.layout.delete()
-    assert len(_stacks(self)) == 1
-
-    self.c.layout.add()
-    assert _stacks(self) == [["one", "three", "two"], []]
-
-    self.c.layout.rotate()
-    assert _stacks(self) == [[], ["one", "three", "two"]]
-
-
-@Xephyr(False, StackConfig())
-def test_stack_cmd_down(self):
-    self.c.layout.down()
-
-
-@Xephyr(False, StackConfig())
-def test_stack_addremove(self):
-    one = self.testWindow("one")
-    self.c.layout.next()
-    two = self.testWindow("two")
-    three = self.testWindow("three")
-    assert _stacks(self) == [['one'], ['three', 'two']]
-    assert self.c.layout.info()["current_stack"] == 1
-    self.kill(three)
-    assert self.c.layout.info()["current_stack"] == 1
-    self.kill(two)
-    assert self.c.layout.info()["current_stack"] == 0
-    self.c.layout.next()
-    two = self.testWindow("two")
-    self.c.layout.next()
-    assert self.c.layout.info()["current_stack"] == 0
-    self.kill(one)
-    assert self.c.layout.info()["current_stack"] == 1
-
-
-@Xephyr(False, StackConfig())
-def test_stack_rotation(self):
-    self.c.layout.delete()
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
-    assert _stacks(self) == [["three", "two", "one"]]
-    self.c.layout.down()
-    assert _stacks(self) == [["one", "three", "two"]]
-    self.c.layout.up()
-    assert _stacks(self) == [["three", "two", "one"]]
-    self.c.layout.down()
-    self.c.layout.down()
-    assert _stacks(self) == [["two", "one", "three"]]
-
-
-@Xephyr(False, StackConfig())
-def test_stack_nextprev(self):
-    self.c.layout.add()
-    one = self.testWindow("one")
-    two = self.testWindow("two")
-    three = self.testWindow("three")
-
-    assert self.c.groups()["a"]["focus"] == "three"
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == "one"
-
-    self.c.layout.previous()
-    assert self.c.groups()["a"]["focus"] == "three"
-    self.c.layout.previous()
-    assert self.c.groups()["a"]["focus"] == "two"
-
-    self.c.layout.next()
-    self.c.layout.next()
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == "two"
-
-    self.kill(three)
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == "one"
-    self.c.layout.previous()
-    assert self.c.groups()["a"]["focus"] == "two"
-    self.c.layout.next()
-    self.kill(two)
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == "one"
-
-    self.kill(one)
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == None
-    self.c.layout.previous()
-    assert self.c.groups()["a"]["focus"] == None
-
-
-@Xephyr(False, StackConfig())
-def test_stack_window_removal(self):
-    self.c.layout.next()
-    one = self.testWindow("one")
-    two = self.testWindow("two")
-    self.c.layout.down()
-    self.kill(two)
-
-
-@Xephyr(False, StackConfig())
-def test_stack_split(self):
-    one = self.testWindow("one")
-    two = self.testWindow("two")
-    three = self.testWindow("three")
-    stacks = self.c.layout.info()["stacks"]
+    qtile.c.layout.delete()
+    assert len(_stacks(qtile)) == 1
+
+    qtile.c.layout.add()
+    assert _stacks(qtile) == [["one", "three", "two"], []]
+
+    qtile.c.layout.rotate()
+    assert _stacks(qtile) == [[], ["one", "three", "two"]]
+
+
+@stack_config
+def test_stack_cmd_down(qtile):
+    qtile.c.layout.down()
+
+
+@stack_config
+def test_stack_addremove(qtile):
+    one = qtile.testWindow("one")
+    qtile.c.layout.next()
+    two = qtile.testWindow("two")
+    three = qtile.testWindow("three")
+    assert _stacks(qtile) == [['one'], ['three', 'two']]
+    assert qtile.c.layout.info()["current_stack"] == 1
+    qtile.kill_window(three)
+    assert qtile.c.layout.info()["current_stack"] == 1
+    qtile.kill_window(two)
+    assert qtile.c.layout.info()["current_stack"] == 0
+    qtile.c.layout.next()
+    two = qtile.testWindow("two")
+    qtile.c.layout.next()
+    assert qtile.c.layout.info()["current_stack"] == 0
+    qtile.kill_window(one)
+    assert qtile.c.layout.info()["current_stack"] == 1
+
+
+@stack_config
+def test_stack_rotation(qtile):
+    qtile.c.layout.delete()
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
+    assert _stacks(qtile) == [["three", "two", "one"]]
+    qtile.c.layout.down()
+    assert _stacks(qtile) == [["one", "three", "two"]]
+    qtile.c.layout.up()
+    assert _stacks(qtile) == [["three", "two", "one"]]
+    qtile.c.layout.down()
+    qtile.c.layout.down()
+    assert _stacks(qtile) == [["two", "one", "three"]]
+
+
+@stack_config
+def test_stack_nextprev(qtile):
+    qtile.c.layout.add()
+    one = qtile.testWindow("one")
+    two = qtile.testWindow("two")
+    three = qtile.testWindow("three")
+
+    assert qtile.c.groups()["a"]["focus"] == "three"
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] == "one"
+
+    qtile.c.layout.previous()
+    assert qtile.c.groups()["a"]["focus"] == "three"
+    qtile.c.layout.previous()
+    assert qtile.c.groups()["a"]["focus"] == "two"
+
+    qtile.c.layout.next()
+    qtile.c.layout.next()
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] == "two"
+
+    qtile.kill_window(three)
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] == "one"
+    qtile.c.layout.previous()
+    assert qtile.c.groups()["a"]["focus"] == "two"
+    qtile.c.layout.next()
+    qtile.kill_window(two)
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] == "one"
+
+    qtile.kill_window(one)
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] is None
+    qtile.c.layout.previous()
+    assert qtile.c.groups()["a"]["focus"] is None
+
+
+@stack_config
+def test_stack_window_removal(qtile):
+    qtile.c.layout.next()
+    one = qtile.testWindow("one")
+    two = qtile.testWindow("two")
+    qtile.c.layout.down()
+    qtile.kill_window(two)
+
+
+@stack_config
+def test_stack_split(qtile):
+    one = qtile.testWindow("one")
+    two = qtile.testWindow("two")
+    three = qtile.testWindow("three")
+    stacks = qtile.c.layout.info()["stacks"]
     assert not stacks[1]["split"]
-    self.c.layout.toggle_split()
-    stacks = self.c.layout.info()["stacks"]
+    qtile.c.layout.toggle_split()
+    stacks = qtile.c.layout.info()["stacks"]
     assert stacks[1]["split"]
 
 
-@Xephyr(False, StackConfig())
-def test_stack_shuffle(self):
-    self.c.next_layout()
-    one = self.testWindow("one")
-    two = self.testWindow("two")
-    three = self.testWindow("three")
+@stack_config
+def test_stack_shuffle(qtile):
+    qtile.c.next_layout()
+    one = qtile.testWindow("one")
+    two = qtile.testWindow("two")
+    three = qtile.testWindow("three")
 
-    stack = self.c.layout.info()["stacks"][0]
+    stack = qtile.c.layout.info()["stacks"][0]
     assert stack["clients"][stack["current"]] == "three"
     for i in range(5):
-        self.c.layout.shuffle_up()
-        stack = self.c.layout.info()["stacks"][0]
+        qtile.c.layout.shuffle_up()
+        stack = qtile.c.layout.info()["stacks"][0]
         assert stack["clients"][stack["current"]] == "three"
     for i in range(5):
-        self.c.layout.shuffle_down()
-        stack = self.c.layout.info()["stacks"][0]
+        qtile.c.layout.shuffle_down()
+        stack = qtile.c.layout.info()["stacks"][0]
         assert stack["clients"][stack["current"]] == "three"
 
 
-@Xephyr(False, StackConfig())
-def test_stack_client_to(self):
-    one = self.testWindow("one")
-    two = self.testWindow("two")
-    assert self.c.layout.info()["stacks"][0]["clients"] == ["one"]
-    self.c.layout.client_to_previous()
-    assert self.c.layout.info()["stacks"][0]["clients"] == ["two", "one"]
-    self.c.layout.client_to_previous()
-    assert self.c.layout.info()["stacks"][0]["clients"] == ["one"]
-    assert self.c.layout.info()["stacks"][1]["clients"] == ["two"]
-    self.c.layout.client_to_next()
-    assert self.c.layout.info()["stacks"][0]["clients"] == ["two", "one"]
-
-
-@Xephyr(False, StackConfig())
-def test_stack_info(self):
-    one = self.testWindow("one")
-    assert self.c.layout.info()["stacks"]
+@stack_config
+def test_stack_client_to(qtile):
+    one = qtile.testWindow("one")
+    two = qtile.testWindow("two")
+    assert qtile.c.layout.info()["stacks"][0]["clients"] == ["one"]
+    qtile.c.layout.client_to_previous()
+    assert qtile.c.layout.info()["stacks"][0]["clients"] == ["two", "one"]
+    qtile.c.layout.client_to_previous()
+    assert qtile.c.layout.info()["stacks"][0]["clients"] == ["one"]
+    assert qtile.c.layout.info()["stacks"][1]["clients"] == ["two"]
+    qtile.c.layout.client_to_next()
+    assert qtile.c.layout.info()["stacks"][0]["clients"] == ["two", "one"]
+
+
+@stack_config
+def test_stack_info(qtile):
+    one = qtile.testWindow("one")
+    assert qtile.c.layout.info()["stacks"]
diff --git a/test/layouts/test_tile.py b/test/layouts/test_tile.py
--- a/test/layouts/test_tile.py
+++ b/test/layouts/test_tile.py
@@ -25,10 +25,12 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
+from ..conftest import no_xinerama
 
 
 class TileConfig:
@@ -43,7 +45,7 @@ class TileConfig:
     layouts = [
         layout.Tile(),
         layout.Tile(masterWindows=2)
-        ]
+    ]
     floating_layout = libqtile.layout.floating.Floating()
     keys = []
     mouse = []
@@ -51,64 +53,68 @@ class TileConfig:
     follow_mouse_focus = False
 
 
-@Xephyr(False, TileConfig())
-def test_tile_updown(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
-    assert self.c.layout.info()["clients"] == ["three", "two", "one"]
-    self.c.layout.down()
-    assert self.c.layout.info()["clients"] == ["two", "one", "three"]
-    self.c.layout.up()
-    assert self.c.layout.info()["clients"] == ["three", "two", "one"]
+tile_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [TileConfig], indirect=True)(x))
+
+
+@tile_config
+def test_tile_updown(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
+    assert qtile.c.layout.info()["clients"] == ["three", "two", "one"]
+    qtile.c.layout.down()
+    assert qtile.c.layout.info()["clients"] == ["two", "one", "three"]
+    qtile.c.layout.up()
+    assert qtile.c.layout.info()["clients"] == ["three", "two", "one"]
 
 
-@Xephyr(False, TileConfig())
-def test_tile_nextprev(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
+@tile_config
+def test_tile_nextprev(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
 
-    assert self.c.layout.info()["clients"] == ["three", "two", "one"]
-    assert self.c.groups()["a"]["focus"] == "three"
+    assert qtile.c.layout.info()["clients"] == ["three", "two", "one"]
+    assert qtile.c.groups()["a"]["focus"] == "three"
 
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == "two"
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] == "two"
 
-    self.c.layout.previous()
-    assert self.c.groups()["a"]["focus"] == "three"
+    qtile.c.layout.previous()
+    assert qtile.c.groups()["a"]["focus"] == "three"
 
-    self.c.layout.previous()
-    assert self.c.groups()["a"]["focus"] == "one"
+    qtile.c.layout.previous()
+    assert qtile.c.groups()["a"]["focus"] == "one"
 
-    self.c.layout.next()
-    self.c.layout.next()
-    self.c.layout.next()
-    assert self.c.groups()["a"]["focus"] == "one"
+    qtile.c.layout.next()
+    qtile.c.layout.next()
+    qtile.c.layout.next()
+    assert qtile.c.groups()["a"]["focus"] == "one"
 
 
-@Xephyr(False, TileConfig())
-def test_tile_master_and_slave(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    self.testWindow("three")
+@tile_config
+def test_tile_master_and_slave(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    qtile.testWindow("three")
 
-    assert self.c.layout.info()["master"] == ["three"]
-    assert self.c.layout.info()["slave"] == ["two", "one"]
+    assert qtile.c.layout.info()["master"] == ["three"]
+    assert qtile.c.layout.info()["slave"] == ["two", "one"]
 
-    self.c.next_layout()
-    assert self.c.layout.info()["master"] == ["three", "two"]
-    assert self.c.layout.info()["slave"] == ["one"]
+    qtile.c.next_layout()
+    assert qtile.c.layout.info()["master"] == ["three", "two"]
+    assert qtile.c.layout.info()["slave"] == ["one"]
 
 
-@Xephyr(False, TileConfig())
-def test_tile_remove(self):
-    one = self.testWindow("one")
-    self.testWindow("two")
-    three = self.testWindow("three")
+@tile_config
+def test_tile_remove(qtile):
+    one = qtile.testWindow("one")
+    qtile.testWindow("two")
+    three = qtile.testWindow("three")
 
-    assert self.c.layout.info()["master"] == ["three"]
-    self.kill(one)
-    assert self.c.layout.info()["master"] == ["three"]
-    self.kill(three)
-    assert self.c.layout.info()["master"] == ["two"]
+    assert qtile.c.layout.info()["master"] == ["three"]
+    qtile.kill_window(one)
+    assert qtile.c.layout.info()["master"] == ["three"]
+    qtile.kill_window(three)
+    assert qtile.c.layout.info()["master"] == ["two"]
diff --git a/test/layouts/test_verticaltile.py b/test/layouts/test_verticaltile.py
--- a/test/layouts/test_verticaltile.py
+++ b/test/layouts/test_verticaltile.py
@@ -25,11 +25,13 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
-from .layout_utils import assertDimensions, assertFocused, assertFocusPath
+from .layout_utils import assertDimensions
+from ..conftest import no_xinerama
 
 
 class VerticalTileConfig(object):
@@ -50,22 +52,26 @@ class VerticalTileConfig(object):
     screens = []
 
 
-@Xephyr(False, VerticalTileConfig())
-def test_verticaltile_simple(self):
-    self.testWindow("one")
-    assertDimensions(self, 0, 0, 800, 600)
-    self.testWindow("two")
-    assertDimensions(self, 0, 300, 798, 298)
-    self.testWindow("three")
-    assertDimensions(self, 0, 400, 798, 198)
+verticaltile_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [VerticalTileConfig], indirect=True)(x))
+
+
+@verticaltile_config
+def test_verticaltile_simple(qtile):
+    qtile.testWindow("one")
+    assertDimensions(qtile, 0, 0, 800, 600)
+    qtile.testWindow("two")
+    assertDimensions(qtile, 0, 300, 798, 298)
+    qtile.testWindow("three")
+    assertDimensions(qtile, 0, 400, 798, 198)
 
 
-@Xephyr(False, VerticalTileConfig())
-def test_verticaltile_maximize(self):
-    self.testWindow("one")
-    assertDimensions(self, 0, 0, 800, 600)
-    self.testWindow("two")
-    assertDimensions(self, 0, 300, 798, 298)
+@verticaltile_config
+def test_verticaltile_maximize(qtile):
+    qtile.testWindow("one")
+    assertDimensions(qtile, 0, 0, 800, 600)
+    qtile.testWindow("two")
+    assertDimensions(qtile, 0, 300, 798, 298)
     # Maximize the bottom layout, taking 75% of space
-    self.c.layout.maximize()
-    assertDimensions(self, 0, 150, 798, 448)
+    qtile.c.layout.maximize()
+    assertDimensions(qtile, 0, 150, 798, 448)
diff --git a/test/layouts/test_xmonad.py b/test/layouts/test_xmonad.py
--- a/test/layouts/test_xmonad.py
+++ b/test/layouts/test_xmonad.py
@@ -18,11 +18,13 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
 from .layout_utils import assertDimensions, assertFocused
+from ..conftest import no_xinerama
 
 
 class MonadTallConfig(object):
@@ -41,6 +43,10 @@ class MonadTallConfig(object):
     follow_mouse_focus = False
 
 
+monadtall_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [MonadTallConfig], indirect=True)(x))
+
+
 class MonadTallMarginsConfig(object):
     auto_fullscreen = True
     main = None
@@ -57,247 +63,251 @@ class MonadTallMarginsConfig(object):
     follow_mouse_focus = False
 
 
-@Xephyr(False, MonadTallConfig())
-def test_add_clients(self):
-    self.testWindow('one')
-    self.testWindow('two')
-    assert self.c.layout.info()["main"] == 'one'
-    assert self.c.layout.info()["secondary"] == ['two']
-    assertFocused(self, 'two')
+monadtallmargins_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [MonadTallMarginsConfig], indirect=True)(x))
+
+
+@monadtall_config
+def test_add_clients(qtile):
+    qtile.testWindow('one')
+    qtile.testWindow('two')
+    assert qtile.c.layout.info()["main"] == 'one'
+    assert qtile.c.layout.info()["secondary"] == ['two']
+    assertFocused(qtile, 'two')
 
-    self.testWindow('three')
-    assert self.c.layout.info()["main"] == 'one'
-    assert self.c.layout.info()["secondary"] == ['two', 'three']
-    assertFocused(self, 'three')
+    qtile.testWindow('three')
+    assert qtile.c.layout.info()["main"] == 'one'
+    assert qtile.c.layout.info()["secondary"] == ['two', 'three']
+    assertFocused(qtile, 'three')
 
-    self.c.layout.previous()
-    assertFocused(self, 'two')
+    qtile.c.layout.previous()
+    assertFocused(qtile, 'two')
 
-    self.testWindow('four')
-    assert self.c.layout.info()["main"] == 'one'
-    assert self.c.layout.info()["secondary"] == ['two', 'four', 'three']
-    assertFocused(self, 'four')
+    qtile.testWindow('four')
+    assert qtile.c.layout.info()["main"] == 'one'
+    assert qtile.c.layout.info()["secondary"] == ['two', 'four', 'three']
+    assertFocused(qtile, 'four')
 
 
-@Xephyr(False, MonadTallMarginsConfig())
-def test_margins(self):
-    self.testWindow('one')
-    assertDimensions(self, 4, 4, 788, 588)
+@monadtallmargins_config
+def test_margins(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 4, 4, 788, 588)
 
-    self.testWindow('two')
-    assertFocused(self, 'two')
-    assertDimensions(self, 404, 4, 388, 588)
+    qtile.testWindow('two')
+    assertFocused(qtile, 'two')
+    assertDimensions(qtile, 404, 4, 388, 588)
 
-    self.c.layout.previous()
-    assertFocused(self, 'one')
-    assertDimensions(self, 4, 4, 392, 588)
+    qtile.c.layout.previous()
+    assertFocused(qtile, 'one')
+    assertDimensions(qtile, 4, 4, 392, 588)
 
 
-@Xephyr(False, MonadTallConfig())
-def test_growmain_solosecondary(self):
-    self.testWindow('one')
-    assertDimensions(self, 0, 0, 796, 596)
+@monadtall_config
+def test_growmain_solosecondary(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 0, 0, 796, 596)
 
-    self.testWindow('two')
-    self.c.layout.previous()
-    assertFocused(self, 'one')
+    qtile.testWindow('two')
+    qtile.c.layout.previous()
+    assertFocused(qtile, 'one')
 
-    assertDimensions(self, 0, 0, 396, 596)
-    self.c.layout.grow()
+    assertDimensions(qtile, 0, 0, 396, 596)
+    qtile.c.layout.grow()
     # Grows 5% of 800 = 40 pixels
-    assertDimensions(self, 0, 0, 436, 596)
-    self.c.layout.shrink()
-    assertDimensions(self, 0, 0, 396, 596)
+    assertDimensions(qtile, 0, 0, 436, 596)
+    qtile.c.layout.shrink()
+    assertDimensions(qtile, 0, 0, 396, 596)
 
     # Max width is 75% of 800 = 600 pixels
     for _ in range(10):
-        self.c.layout.grow()
-    assertDimensions(self, 0, 0, 596, 596)
+        qtile.c.layout.grow()
+    assertDimensions(qtile, 0, 0, 596, 596)
 
     # Min width is 25% of 800 = 200 pixels
     for _ in range(10):
-        self.c.layout.shrink()
-    assertDimensions(self, 0, 0, 196, 596)
+        qtile.c.layout.shrink()
+    assertDimensions(qtile, 0, 0, 196, 596)
 
 
-@Xephyr(False, MonadTallConfig())
-def test_growmain_multiplesecondary(self):
-    self.testWindow('one')
-    assertDimensions(self, 0, 0, 796, 596)
+@monadtall_config
+def test_growmain_multiplesecondary(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 0, 0, 796, 596)
 
-    self.testWindow('two')
-    self.testWindow('three')
-    self.c.layout.previous()
-    self.c.layout.previous()
-    assertFocused(self, 'one')
+    qtile.testWindow('two')
+    qtile.testWindow('three')
+    qtile.c.layout.previous()
+    qtile.c.layout.previous()
+    assertFocused(qtile, 'one')
 
-    assertDimensions(self, 0, 0, 396, 596)
-    self.c.layout.grow()
+    assertDimensions(qtile, 0, 0, 396, 596)
+    qtile.c.layout.grow()
     # Grows 5% of 800 = 40 pixels
-    assertDimensions(self, 0, 0, 436, 596)
-    self.c.layout.shrink()
-    assertDimensions(self, 0, 0, 396, 596)
+    assertDimensions(qtile, 0, 0, 436, 596)
+    qtile.c.layout.shrink()
+    assertDimensions(qtile, 0, 0, 396, 596)
 
     # Max width is 75% of 800 = 600 pixels
     for _ in range(10):
-        self.c.layout.grow()
-    assertDimensions(self, 0, 0, 596, 596)
+        qtile.c.layout.grow()
+    assertDimensions(qtile, 0, 0, 596, 596)
 
     # Min width is 25% of 800 = 200 pixels
     for _ in range(10):
-        self.c.layout.shrink()
-    assertDimensions(self, 0, 0, 196, 596)
+        qtile.c.layout.shrink()
+    assertDimensions(qtile, 0, 0, 196, 596)
 
 
-@Xephyr(False, MonadTallConfig())
-def test_growsecondary_solosecondary(self):
-    self.testWindow('one')
-    assertDimensions(self, 0, 0, 796, 596)
+@monadtall_config
+def test_growsecondary_solosecondary(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 0, 0, 796, 596)
 
-    self.testWindow('two')
-    assertFocused(self, 'two')
+    qtile.testWindow('two')
+    assertFocused(qtile, 'two')
 
-    assertDimensions(self, 400, 0, 396, 596)
-    self.c.layout.grow()
+    assertDimensions(qtile, 400, 0, 396, 596)
+    qtile.c.layout.grow()
     # Grows 5% of 800 = 40 pixels
-    assertDimensions(self, 360, 0, 436, 596)
-    self.c.layout.shrink()
-    assertDimensions(self, 400, 0, 396, 596)
+    assertDimensions(qtile, 360, 0, 436, 596)
+    qtile.c.layout.shrink()
+    assertDimensions(qtile, 400, 0, 396, 596)
 
     # Max width is 75% of 800 = 600 pixels
     for _ in range(10):
-        self.c.layout.grow()
-    assertDimensions(self, 200, 0, 596, 596)
+        qtile.c.layout.grow()
+    assertDimensions(qtile, 200, 0, 596, 596)
 
     # Min width is 25% of 800 = 200 pixels
     for _ in range(10):
-        self.c.layout.shrink()
-    assertDimensions(self, 600, 0, 196, 596)
+        qtile.c.layout.shrink()
+    assertDimensions(qtile, 600, 0, 196, 596)
 
 
-@Xephyr(False, MonadTallConfig())
-def test_growsecondary_multiplesecondary(self):
-    self.testWindow('one')
-    assertDimensions(self, 0, 0, 796, 596)
+@monadtall_config
+def test_growsecondary_multiplesecondary(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 0, 0, 796, 596)
 
-    self.testWindow('two')
-    self.testWindow('three')
-    self.c.layout.previous()
-    assertFocused(self, 'two')
+    qtile.testWindow('two')
+    qtile.testWindow('three')
+    qtile.c.layout.previous()
+    assertFocused(qtile, 'two')
 
-    assertDimensions(self, 400, 0, 396, 296)
+    assertDimensions(qtile, 400, 0, 396, 296)
     # Grow 20 pixels
-    self.c.layout.grow()
-    assertDimensions(self, 400, 0, 396, 316)
-    self.c.layout.shrink()
-    assertDimensions(self, 400, 0, 396, 296)
+    qtile.c.layout.grow()
+    assertDimensions(qtile, 400, 0, 396, 316)
+    qtile.c.layout.shrink()
+    assertDimensions(qtile, 400, 0, 396, 296)
 
     # Min height of other is 85 pixels, leaving 515
     for _ in range(20):
-        self.c.layout.grow()
-    assertDimensions(self, 400, 0, 396, 511)
+        qtile.c.layout.grow()
+    assertDimensions(qtile, 400, 0, 396, 511)
 
-    # Min height of self is 85 pixels
+    # Min height of qtile is 85 pixels
     for _ in range(40):
-        self.c.layout.shrink()
-    assertDimensions(self, 400, 0, 396, 85)
+        qtile.c.layout.shrink()
+    assertDimensions(qtile, 400, 0, 396, 85)
 
 
-@Xephyr(False, MonadTallConfig())
-def test_flip(self):
-    self.testWindow('one')
-    self.testWindow('two')
-    self.testWindow('three')
+@monadtall_config
+def test_flip(qtile):
+    qtile.testWindow('one')
+    qtile.testWindow('two')
+    qtile.testWindow('three')
 
     # Check all the dimensions
-    self.c.layout.next()
-    assertFocused(self, 'one')
-    assertDimensions(self, 0, 0, 396, 596)
+    qtile.c.layout.next()
+    assertFocused(qtile, 'one')
+    assertDimensions(qtile, 0, 0, 396, 596)
 
-    self.c.layout.next()
-    assertFocused(self, 'two')
-    assertDimensions(self, 400, 0, 396, 296)
+    qtile.c.layout.next()
+    assertFocused(qtile, 'two')
+    assertDimensions(qtile, 400, 0, 396, 296)
 
-    self.c.layout.next()
-    assertFocused(self, 'three')
-    assertDimensions(self, 400, 300, 396, 296)
+    qtile.c.layout.next()
+    assertFocused(qtile, 'three')
+    assertDimensions(qtile, 400, 300, 396, 296)
 
     # Now flip it and do it again
-    self.c.layout.flip()
+    qtile.c.layout.flip()
 
-    self.c.layout.next()
-    assertFocused(self, 'one')
-    assertDimensions(self, 400, 0, 396, 596)
+    qtile.c.layout.next()
+    assertFocused(qtile, 'one')
+    assertDimensions(qtile, 400, 0, 396, 596)
 
-    self.c.layout.next()
-    assertFocused(self, 'two')
-    assertDimensions(self, 0, 0, 396, 296)
+    qtile.c.layout.next()
+    assertFocused(qtile, 'two')
+    assertDimensions(qtile, 0, 0, 396, 296)
 
-    self.c.layout.next()
-    assertFocused(self, 'three')
-    assertDimensions(self, 0, 300, 396, 296)
+    qtile.c.layout.next()
+    assertFocused(qtile, 'three')
+    assertDimensions(qtile, 0, 300, 396, 296)
 
 
-@Xephyr(False, MonadTallConfig())
-def test_shuffle(self):
-    self.testWindow('one')
-    self.testWindow('two')
-    self.testWindow('three')
-    self.testWindow('four')
+@monadtall_config
+def test_shuffle(qtile):
+    qtile.testWindow('one')
+    qtile.testWindow('two')
+    qtile.testWindow('three')
+    qtile.testWindow('four')
 
-    assert self.c.layout.info()['main'] == 'one'
-    assert self.c.layout.info()['secondary'] == ['two', 'three', 'four']
+    assert qtile.c.layout.info()['main'] == 'one'
+    assert qtile.c.layout.info()['secondary'] == ['two', 'three', 'four']
 
-    self.c.layout.shuffle_up()
-    assert self.c.layout.info()['main'] == 'one'
-    assert self.c.layout.info()['secondary'] == ['two', 'four', 'three']
+    qtile.c.layout.shuffle_up()
+    assert qtile.c.layout.info()['main'] == 'one'
+    assert qtile.c.layout.info()['secondary'] == ['two', 'four', 'three']
 
-    self.c.layout.shuffle_up()
-    assert self.c.layout.info()['main'] == 'one'
-    assert self.c.layout.info()['secondary'] == ['four', 'two', 'three']
+    qtile.c.layout.shuffle_up()
+    assert qtile.c.layout.info()['main'] == 'one'
+    assert qtile.c.layout.info()['secondary'] == ['four', 'two', 'three']
 
-    self.c.layout.shuffle_up()
-    assert self.c.layout.info()['main'] == 'four'
-    assert self.c.layout.info()['secondary'] == ['one', 'two', 'three']
+    qtile.c.layout.shuffle_up()
+    assert qtile.c.layout.info()['main'] == 'four'
+    assert qtile.c.layout.info()['secondary'] == ['one', 'two', 'three']
 
 
-@Xephyr(False, MonadTallConfig())
-def test_swap(self):
-    self.testWindow('one')
-    self.testWindow('two')
-    self.testWindow('three')
-    self.testWindow('focused')
+@monadtall_config
+def test_swap(qtile):
+    qtile.testWindow('one')
+    qtile.testWindow('two')
+    qtile.testWindow('three')
+    qtile.testWindow('focused')
 
-    assert self.c.layout.info()['main'] == 'one'
-    assert self.c.layout.info()['secondary'] == ['two', 'three', 'focused']
+    assert qtile.c.layout.info()['main'] == 'one'
+    assert qtile.c.layout.info()['secondary'] == ['two', 'three', 'focused']
 
     # Swap a secondary left, left aligned
-    self.c.layout.swap_left()
-    assert self.c.layout.info()['main'] == 'focused'
-    assert self.c.layout.info()['secondary'] == ['two', 'three', 'one']
+    qtile.c.layout.swap_left()
+    assert qtile.c.layout.info()['main'] == 'focused'
+    assert qtile.c.layout.info()['secondary'] == ['two', 'three', 'one']
 
     # Swap a main right, left aligned
-    self.c.layout.swap_right()
-    assert self.c.layout.info()['main'] == 'two'
-    assert self.c.layout.info()['secondary'] == ['focused', 'three', 'one']
+    qtile.c.layout.swap_right()
+    assert qtile.c.layout.info()['main'] == 'two'
+    assert qtile.c.layout.info()['secondary'] == ['focused', 'three', 'one']
 
     # flip over
-    self.c.layout.flip()
-    self.c.layout.shuffle_down()
-    assert self.c.layout.info()['main'] == 'two'
-    assert self.c.layout.info()['secondary'] == ['three', 'focused', 'one']
+    qtile.c.layout.flip()
+    qtile.c.layout.shuffle_down()
+    assert qtile.c.layout.info()['main'] == 'two'
+    assert qtile.c.layout.info()['secondary'] == ['three', 'focused', 'one']
 
     # Swap secondary right, right aligned
-    self.c.layout.swap_right()
-    assert self.c.layout.info()['main'] == 'focused'
-    assert self.c.layout.info()['secondary'] == ['three', 'two', 'one']
+    qtile.c.layout.swap_right()
+    assert qtile.c.layout.info()['main'] == 'focused'
+    assert qtile.c.layout.info()['secondary'] == ['three', 'two', 'one']
 
     # Swap main left, right aligned
-    self.c.layout.swap_left()
-    assert self.c.layout.info()['main'] == 'three'
-    assert self.c.layout.info()['secondary'] == ['focused', 'two', 'one']
+    qtile.c.layout.swap_left()
+    assert qtile.c.layout.info()['main'] == 'three'
+    assert qtile.c.layout.info()['secondary'] == ['focused', 'two', 'one']
 
     # Do swap main
-    self.c.layout.swap_main()
-    assert self.c.layout.info()['main'] == 'focused'
-    assert self.c.layout.info()['secondary'] == ['three', 'two', 'one']
+    qtile.c.layout.swap_main()
+    assert qtile.c.layout.info()['main'] == 'focused'
+    assert qtile.c.layout.info()['secondary'] == ['three', 'two', 'one']
diff --git a/test/layouts/test_zoomy.py b/test/layouts/test_zoomy.py
--- a/test/layouts/test_zoomy.py
+++ b/test/layouts/test_zoomy.py
@@ -25,11 +25,13 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 from libqtile import layout
 import libqtile.manager
 import libqtile.config
-from ..utils import Xephyr
 from .layout_utils import assertDimensions, assertFocusPath
+from ..conftest import no_xinerama
 
 
 class ZoomyConfig:
@@ -40,20 +42,24 @@ class ZoomyConfig:
     ]
     layouts = [
         layout.Zoomy(columnwidth=200),
-        ]
+    ]
     floating_layout = libqtile.layout.floating.Floating()
     keys = []
     mouse = []
     screens = []
 
 
-@Xephyr(False, ZoomyConfig())
-def test_zoomy_one(self):
-    self.testWindow('one')
-    assertDimensions(self, 0, 0, 600, 600)
-    self.testWindow('two')
-    assertDimensions(self, 0, 0, 600, 600)
-    self.testWindow('three')
-    assertDimensions(self, 0, 0, 600, 600)
-    assertFocusPath(self, 'two', 'one', 'three')
+zoomy_config = lambda x: \
+    no_xinerama(pytest.mark.parametrize("qtile", [ZoomyConfig], indirect=True)(x))
+
+
+@zoomy_config
+def test_zoomy_one(qtile):
+    qtile.testWindow('one')
+    assertDimensions(qtile, 0, 0, 600, 600)
+    qtile.testWindow('two')
+    assertDimensions(qtile, 0, 0, 600, 600)
+    qtile.testWindow('three')
+    assertDimensions(qtile, 0, 0, 600, 600)
+    assertFocusPath(qtile, 'two', 'one', 'three')
     # TODO(pc) find a way to check size of inactive windows
diff --git a/test/scripts/window.py b/test/scripts/window.py
--- a/test/scripts/window.py
+++ b/test/scripts/window.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # Copyright (c) 2011 Florian Mounier
 # Copyright (c) 2014 Sean Vig
 #
@@ -18,8 +19,6 @@
 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
-
-#!/usr/bin/env python
 """
     This program is carefully crafted to exercise a number of corner-cases in
     Qtile.
@@ -30,6 +29,7 @@
 import xcffib
 import xcffib.xproto
 
+
 def configure(window):
     window.configure(
         width=100,
@@ -56,17 +56,17 @@ def configure(window):
 screen = conn.get_setup().roots[conn.pref_screen]
 
 window = conn.generate_id()
-background = conn.core.AllocColor(screen.default_colormap, 0x2828, 0x8383, 0xCECE).reply().pixel # Color "#2883ce"
+background = conn.core.AllocColor(screen.default_colormap, 0x2828, 0x8383, 0xCECE).reply().pixel  # Color "#2883ce"
 conn.core.CreateWindow(xcffib.CopyFromParent, window, screen.root,
-        100, 100, 100, 100, 1,
-        xcffib.xproto.WindowClass.InputOutput, screen.root_visual,
-        xcffib.xproto.CW.BackPixel | xcffib.xproto.CW.EventMask,
-        [background, xcffib.xproto.EventMask.StructureNotify | xcffib.xproto.EventMask.Exposure])
+                       100, 100, 100, 100, 1,
+                       xcffib.xproto.WindowClass.InputOutput, screen.root_visual,
+                       xcffib.xproto.CW.BackPixel | xcffib.xproto.CW.EventMask,
+                       [background, xcffib.xproto.EventMask.StructureNotify | xcffib.xproto.EventMask.Exposure])
 
 conn.core.ChangeProperty(xcffib.xproto.PropMode.Replace,
-        window, xcffib.xproto.Atom.WM_NAME,
-        xcffib.xproto.Atom.STRING, 8, len(sys.argv[2]),
-        sys.argv[2])
+                         window, xcffib.xproto.Atom.WM_NAME,
+                         xcffib.xproto.Atom.STRING, 8, len(sys.argv[2]),
+                         sys.argv[2])
 
 wm_protocols = "WM_PROTOCOLS"
 wm_protocols = conn.core.InternAtom(0, len(wm_protocols), wm_protocols).reply().atom
@@ -75,22 +75,22 @@ def configure(window):
 wm_delete_window = conn.core.InternAtom(0, len(wm_delete_window), wm_delete_window).reply().atom
 
 conn.core.ChangeProperty(xcffib.xproto.PropMode.Replace,
-        window, wm_protocols,
-        xcffib.xproto.Atom.ATOM, 32, 1,
-        [wm_delete_window])
+                         window, wm_protocols,
+                         xcffib.xproto.Atom.ATOM, 32, 1,
+                         [wm_delete_window])
 
 conn.core.ConfigureWindow(window,
-        xcffib.xproto.ConfigWindow.X | xcffib.xproto.ConfigWindow.Y |
-        xcffib.xproto.ConfigWindow.Width | xcffib.xproto.ConfigWindow.Height |
-        xcffib.xproto.ConfigWindow.BorderWidth,
-        [0, 0, 100, 100, 1])
+                          xcffib.xproto.ConfigWindow.X | xcffib.xproto.ConfigWindow.Y |
+                          xcffib.xproto.ConfigWindow.Width | xcffib.xproto.ConfigWindow.Height |
+                          xcffib.xproto.ConfigWindow.BorderWidth,
+                          [0, 0, 100, 100, 1])
 conn.core.MapWindow(window)
 conn.flush()
 conn.core.ConfigureWindow(window,
-        xcffib.xproto.ConfigWindow.X | xcffib.xproto.ConfigWindow.Y |
-        xcffib.xproto.ConfigWindow.Width | xcffib.xproto.ConfigWindow.Height |
-        xcffib.xproto.ConfigWindow.BorderWidth,
-        [0, 0, 100, 100, 1])
+                          xcffib.xproto.ConfigWindow.X | xcffib.xproto.ConfigWindow.Y |
+                          xcffib.xproto.ConfigWindow.Width | xcffib.xproto.ConfigWindow.Height |
+                          xcffib.xproto.ConfigWindow.BorderWidth,
+                          [0, 0, 100, 100, 1])
 
 try:
     while 1:
diff --git a/test/test_bar.py b/test/test_bar.py
--- a/test/test_bar.py
+++ b/test/test_bar.py
@@ -22,17 +22,19 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
 import time
 import six
+
 import libqtile.layout
 import libqtile.bar
 import libqtile.widget
 import libqtile.manager
 import libqtile.config
 import libqtile.confreader
-from .utils import Xephyr
 
-class GBConfig:
+
+class GBConfig(object):
     auto_fullscreen = True
     keys = []
     mouse = []
@@ -59,7 +61,7 @@ class GBConfig:
                     libqtile.widget.MemoryGraph(type="line"),
                     libqtile.widget.SwapGraph(type="box"),
                     libqtile.widget.TextBox(name="text",
-                    background="333333"),
+                                            background="333333"),
                 ],
                 50,
             ),
@@ -80,6 +82,9 @@ class GBConfig:
     main = None
 
 
+gb_config = pytest.mark.parametrize("qtile", [GBConfig], indirect=True)
+
+
 def test_completion():
     c = libqtile.widget.prompt.CommandCompleter(None, True)
     c.reset()
@@ -107,60 +112,61 @@ def test_completion():
     s = "thisisatotallynonexistantpathforsure"
     assert c.complete(s) == s
     assert c.actual() == s
+    c.reset()
 
 
-@Xephyr(True, GBConfig())
-def test_draw(self):
-    self.testWindow("one")
-    b = self.c.bar["bottom"].info()
+@gb_config
+def test_draw(qtile):
+    qtile.testWindow("one")
+    b = qtile.c.bar["bottom"].info()
     assert b["widgets"][0]["name"] == "groupbox"
 
 
-@Xephyr(True, GBConfig())
-def test_prompt(self):
-    assert self.c.widget["prompt"].info()["width"] == 0
-    self.c.spawncmd(":")
-    self.c.widget["prompt"].fake_keypress("a")
-    self.c.widget["prompt"].fake_keypress("Tab")
+@gb_config
+def test_prompt(qtile):
+    assert qtile.c.widget["prompt"].info()["width"] == 0
+    qtile.c.spawncmd(":")
+    qtile.c.widget["prompt"].fake_keypress("a")
+    qtile.c.widget["prompt"].fake_keypress("Tab")
 
-    self.c.spawncmd(":")
-    self.c.widget["prompt"].fake_keypress("slash")
-    self.c.widget["prompt"].fake_keypress("Tab")
+    qtile.c.spawncmd(":")
+    qtile.c.widget["prompt"].fake_keypress("slash")
+    qtile.c.widget["prompt"].fake_keypress("Tab")
 
 
-@Xephyr(True, GBConfig())
-def test_event(self):
-    self.c.group["bb"].toscreen()
+@gb_config
+def test_event(qtile):
+    qtile.c.group["bb"].toscreen()
 
 
-@Xephyr(True, GBConfig())
-def test_textbox(self):
-    assert "text" in self.c.list_widgets()
+@gb_config
+def test_textbox(qtile):
+    assert "text" in qtile.c.list_widgets()
     s = "some text"
-    self.c.widget["text"].update(s)
-    assert self.c.widget["text"].get() == s
+    qtile.c.widget["text"].update(s)
+    assert qtile.c.widget["text"].get() == s
     s = "Aye, much longer string than the initial one"
-    self.c.widget["text"].update(s)
-    assert self.c.widget["text"].get() == s
-    self.c.group["Pppy"].toscreen()
-    self.c.widget["text"].set_font(fontsize=12)
+    qtile.c.widget["text"].update(s)
+    assert qtile.c.widget["text"].get() == s
+    qtile.c.group["Pppy"].toscreen()
+    qtile.c.widget["text"].set_font(fontsize=12)
     time.sleep(3)
 
 
-@Xephyr(True, GBConfig())
-def test_textbox_errors(self):
-    self.c.widget["text"].update(None)
-    self.c.widget["text"].update("".join(chr(i) for i in range(255)))
-    self.c.widget["text"].update("V\xE2r\xE2na\xE7\xEE")
-    self.c.widget["text"].update(six.u("\ua000"))
+@gb_config
+def test_textbox_errors(qtile):
+    qtile.c.widget["text"].update(None)
+    qtile.c.widget["text"].update("".join(chr(i) for i in range(255)))
+    qtile.c.widget["text"].update("V\xE2r\xE2na\xE7\xEE")
+    qtile.c.widget["text"].update(six.u("\ua000"))
 
 
-@Xephyr(True, GBConfig())
-def test_groupbox_button_press(self):
-    self.c.group["ccc"].toscreen()
-    assert self.c.groups()["a"]["screen"] == None
-    self.c.bar["bottom"].fake_button_press(0, "bottom", 10, 10, 1)
-    assert self.c.groups()["a"]["screen"] == 0
+@gb_config
+def test_groupbox_button_press(qtile):
+    qtile.c.group["ccc"].toscreen()
+    assert qtile.c.groups()["a"]["screen"] is None
+    qtile.c.bar["bottom"].fake_button_press(0, "bottom", 10, 10, 1)
+    assert qtile.c.groups()["a"]["screen"] == 0
 
 
 class GeomConf:
@@ -186,6 +192,9 @@ class GeomConf:
     ]
 
 
+geom_config = pytest.mark.parametrize("qtile", [GeomConf], indirect=True)
+
+
 class DBarH(libqtile.bar.Bar):
     def __init__(self, widgets, size):
         libqtile.bar.Bar.__init__(self, widgets, size)
@@ -203,28 +212,28 @@ def __init__(self, length, length_type):
         self.length, self.length_type = length, length_type
 
 
-@Xephyr(True, GeomConf())
-def test_geometry(self):
-    self.testXeyes()
-    g = self.c.screens()[0]["gaps"]
+@geom_config
+def test_geometry(qtile):
+    qtile.testXeyes()
+    g = qtile.c.screens()[0]["gaps"]
     assert g["top"] == (0, 0, 800, 10)
     assert g["bottom"] == (0, 590, 800, 10)
     assert g["left"] == (0, 10, 10, 580)
     assert g["right"] == (790, 10, 10, 580)
-    assert len(self.c.windows()) == 1
-    geom = self.c.windows()[0]
+    assert len(qtile.c.windows()) == 1
+    geom = qtile.c.windows()[0]
     assert geom["x"] == 10
     assert geom["y"] == 10
     assert geom["width"] == 778
     assert geom["height"] == 578
-    internal = self.c.internal_windows()
+    internal = qtile.c.internal_windows()
     assert len(internal) == 4
-    wid = self.c.bar["bottom"].info()["window"]
-    assert self.c.window[wid].inspect()
+    wid = qtile.c.bar["bottom"].info()["window"]
+    assert qtile.c.window[wid].inspect()
 
 
-@Xephyr(True, GeomConf())
-def test_resize(self):
+@geom_config
+def test_resize(qtile):
     def wd(l):
         return [i.length for i in l]
 
@@ -284,7 +293,7 @@ def offy(l):
         assert off(l) == [0, 10, 90]
 
 
-class TestWidget(libqtile.widget.base._Widget):
+class ExampleWidget(libqtile.widget.base._Widget):
     orientations = libqtile.widget.base.ORIENTATION_HORIZONTAL
 
     def __init__(self):
@@ -294,7 +303,7 @@ def draw(self):
         pass
 
 
-class IncompatibleWidgetConf:
+class IncompatibleWidgetConf(object):
     main = None
     keys = []
     mouse = []
@@ -306,7 +315,7 @@ class IncompatibleWidgetConf:
             left=libqtile.bar.Bar(
                 [
                     # This widget doesn't support vertical orientation
-                    TestWidget(),
+                    ExampleWidget(),
                 ],
                 10
             ),
@@ -314,14 +323,16 @@ class IncompatibleWidgetConf:
     ]
 
 
-@Xephyr(True, IncompatibleWidgetConf(), False)
-def test_incompatible_widget(self):
+def test_incompatible_widget(qtile_nospawn):
+    config = IncompatibleWidgetConf
+
     # Ensure that adding a widget that doesn't support the orientation of the
     # bar raises ConfigError
-    self.qtileRaises(libqtile.confreader.ConfigError, IncompatibleWidgetConf())
+    with pytest.raises(libqtile.confreader.ConfigError):
+        qtile_nospawn.create_manager(config)
 
 
-class MultiStretchConf:
+class MultiStretchConf(object):
     main = None
     keys = []
     mouse = []
@@ -341,39 +352,42 @@ class MultiStretchConf:
     ]
 
 
-@Xephyr(True, MultiStretchConf(), False)
-def test_multiple_stretches(self):
+def test_multiple_stretches(qtile_nospawn):
+    config = MultiStretchConf
+
     # Ensure that adding two STRETCH widgets to the same bar raises ConfigError
-    self.qtileRaises(libqtile.confreader.ConfigError, MultiStretchConf())
+    with pytest.raises(libqtile.confreader.ConfigError):
+        qtile_nospawn.create_manager(config)
 
 
-@Xephyr(True, GeomConf(), False)
-def test_basic(self):
-    self.config.screens = [
+def test_basic(qtile_nospawn):
+    config = GeomConf
+    config.screens = [
         libqtile.config.Screen(
             bottom=libqtile.bar.Bar(
                 [
-                    TestWidget(),
+                    ExampleWidget(),
                     libqtile.widget.Spacer(libqtile.bar.STRETCH),
-                    TestWidget()
+                    ExampleWidget()
                 ],
                 10
             )
         )
     ]
-    self.startQtile(self.config)
-    i = self.c.bar["bottom"].info()
+
+    qtile_nospawn.start(config)
+
+    i = qtile_nospawn.c.bar["bottom"].info()
     assert i["widgets"][0]["offset"] == 0
     assert i["widgets"][1]["offset"] == 10
     assert i["widgets"][1]["width"] == 780
     assert i["widgets"][2]["offset"] == 790
     libqtile.hook.clear()
-    self.stopQtile()
 
 
-@Xephyr(True, GeomConf(), False)
-def test_singlespacer(self):
-    self.config.screens = [
+def test_singlespacer(qtile_nospawn):
+    config = GeomConf
+    config.screens = [
         libqtile.config.Screen(
             bottom=libqtile.bar.Bar(
                 [
@@ -383,30 +397,32 @@ def test_singlespacer(self):
             )
         )
     ]
-    self.startQtile(self.config)
-    i = self.c.bar["bottom"].info()
+
+    qtile_nospawn.start(config)
+
+    i = qtile_nospawn.c.bar["bottom"].info()
     assert i["widgets"][0]["offset"] == 0
     assert i["widgets"][0]["width"] == 800
     libqtile.hook.clear()
-    self.stopQtile()
 
 
-@Xephyr(True, GeomConf(), False)
-def test_nospacer(self):
-    self.config.screens = [
+def test_nospacer(qtile_nospawn):
+    config = GeomConf
+    config.screens = [
         libqtile.config.Screen(
             bottom=libqtile.bar.Bar(
                 [
-                    TestWidget(),
-                    TestWidget()
+                    ExampleWidget(),
+                    ExampleWidget()
                 ],
                 10
             )
         )
     ]
-    self.startQtile(self.config)
-    i = self.c.bar["bottom"].info()
+
+    qtile_nospawn.start(config)
+
+    i = qtile_nospawn.c.bar["bottom"].info()
     assert i["widgets"][0]["offset"] == 0
     assert i["widgets"][1]["offset"] == 10
     libqtile.hook.clear()
-    self.stopQtile()
diff --git a/test/test_command.py b/test/test_command.py
--- a/test/test_command.py
+++ b/test/test_command.py
@@ -21,6 +21,8 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 import libqtile
 import libqtile.confreader
 import libqtile.manager
@@ -28,8 +30,6 @@
 import libqtile.layout
 import libqtile.bar
 import libqtile.widget
-from .utils import Xephyr
-from nose.tools import assert_raises
 
 
 class CallConfig(object):
@@ -56,26 +56,29 @@ class CallConfig(object):
     screens = [
         libqtile.config.Screen(
             bottom=libqtile.bar.Bar(
-                        [
-                            libqtile.widget.GroupBox(),
-                        ],
-                        20
-                    ),
+                [
+                    libqtile.widget.GroupBox(),
+                ],
+                20
+            ),
         )
     ]
     main = None
     auto_fullscreen = True
 
 
-@Xephyr(True, CallConfig())
-def test_layout_filter(self):
-    self.testWindow("one")
-    self.testWindow("two")
-    assert self.c.groups()["a"]["focus"] == "two"
-    self.c.simulate_keypress(["control"], "j")
-    assert self.c.groups()["a"]["focus"] == "one"
-    self.c.simulate_keypress(["control"], "k")
-    assert self.c.groups()["a"]["focus"] == "two"
+call_config = pytest.mark.parametrize("qtile", [CallConfig], indirect=True)
+
+
+@call_config
+def test_layout_filter(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
+    assert qtile.c.groups()["a"]["focus"] == "two"
+    qtile.c.simulate_keypress(["control"], "j")
+    assert qtile.c.groups()["a"]["focus"] == "one"
+    qtile.c.simulate_keypress(["control"], "k")
+    assert qtile.c.groups()["a"]["focus"] == "two"
 
 
 class TestCommands(libqtile.command.CommandObject):
@@ -139,7 +142,7 @@ def test_selectors():
 
     g = c.group
     assert isinstance(g, libqtile.command._TGroup)
-    assert g.myselector == None
+    assert g.myselector is None
 
     g = c.group["one"]
     assert isinstance(g, libqtile.command._TGroup)
@@ -174,208 +177,231 @@ class ServerConfig(object):
     screens = [
         libqtile.config.Screen(
             bottom=libqtile.bar.Bar(
-                        [
-                            libqtile.widget.TextBox(name="one"),
-                        ],
-                        20
-                    ),
+                [
+                    libqtile.widget.TextBox(name="one"),
+                ],
+                20
+            ),
         ),
         libqtile.config.Screen(
             bottom=libqtile.bar.Bar(
-                        [
-                            libqtile.widget.TextBox(name="two"),
-                        ],
-                        20
-                    ),
+                [
+                    libqtile.widget.TextBox(name="two"),
+                ],
+                20
+            ),
         )
     ]
     main = None
 
 
-@Xephyr(True, ServerConfig())
-def test_cmd_commands(self):
-    assert self.c.commands()
-    assert self.c.layout.commands()
-    assert self.c.screen.bar["bottom"].commands()
+server_config = pytest.mark.parametrize("qtile", [ServerConfig], indirect=True)
+
+
+@server_config
+def test_cmd_commands(qtile):
+    assert qtile.c.commands()
+    assert qtile.c.layout.commands()
+    assert qtile.c.screen.bar["bottom"].commands()
+
 
+@server_config
+def test_call_unknown(qtile):
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.nonexistent()
 
-@Xephyr(True, ServerConfig())
-def test_call_unknown(self):
-    assert_raises(libqtile.command.CommandError, self.c.nonexistent)
-    assert_raises(libqtile.command.CommandError, self.c.layout.nonexistent)
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.layout.nonexistent()
 
 
-@Xephyr(True, ServerConfig())
-def test_items_qtile(self):
-    v = self.c.items("group")
+@server_config
+def test_items_qtile(qtile):
+    v = qtile.c.items("group")
     assert v[0]
     assert sorted(v[1]) == ["a", "b", "c"]
 
-    assert self.c.items("layout") == (True, [0, 1, 2])
+    assert qtile.c.items("layout") == (True, [0, 1, 2])
 
-    v = self.c.items("widget")
+    v = qtile.c.items("widget")
     assert not v[0]
     assert sorted(v[1]) == ['one', 'two']
 
-    assert self.c.items("bar") == (False, ["bottom"])
-    t, lst = self.c.items("window")
+    assert qtile.c.items("bar") == (False, ["bottom"])
+    t, lst = qtile.c.items("window")
     assert t
     assert len(lst) == 2
-    assert self.c.window[lst[0]]
-    assert self.c.items("screen") == (True, [0, 1])
+    assert qtile.c.window[lst[0]]
+    assert qtile.c.items("screen") == (True, [0, 1])
 
 
-@Xephyr(True, ServerConfig())
-def test_select_qtile(self):
-    assert self.c.foo.selectors == []
-    assert self.c.layout.info()["group"] == "a"
-    assert len(self.c.layout.info()["stacks"]) == 1
-    assert len(self.c.layout[2].info()["stacks"]) == 3
-    assert_raises(libqtile.command.CommandError, self.c.layout[99].info)
+@server_config
+def test_select_qtile(qtile):
+    assert qtile.c.foo.selectors == []
+    assert qtile.c.layout.info()["group"] == "a"
+    assert len(qtile.c.layout.info()["stacks"]) == 1
+    assert len(qtile.c.layout[2].info()["stacks"]) == 3
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.layout[99].info()
 
-    assert self.c.group.info()["name"] == "a"
-    assert self.c.group["c"].info()["name"] == "c"
-    assert_raises(
-        libqtile.command.CommandError, self.c.group["nonexistent"].info)
+    assert qtile.c.group.info()["name"] == "a"
+    assert qtile.c.group["c"].info()["name"] == "c"
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.group["nonexistent"].info()
 
-    assert self.c.widget["one"].info()["name"] == "one"
-    assert_raises(libqtile.command.CommandError, self.c.widget.info)
+    assert qtile.c.widget["one"].info()["name"] == "one"
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.widget.info()
 
-    assert self.c.bar["bottom"].info()["position"] == "bottom"
+    assert qtile.c.bar["bottom"].info()["position"] == "bottom"
 
-    win = self.testWindow("one")
-    wid = self.c.window.info()["id"]
-    assert self.c.window[wid].info()["id"] == wid
+    win = qtile.testWindow("one")
+    wid = qtile.c.window.info()["id"]
+    assert qtile.c.window[wid].info()["id"] == wid
 
-    assert self.c.screen.info()["index"] == 0
-    assert self.c.screen[1].info()["index"] == 1
-    assert_raises(libqtile.command.CommandError, self.c.screen[22].info)
-    assert_raises(libqtile.command.CommandError, self.c.screen["foo"].info)
+    assert qtile.c.screen.info()["index"] == 0
+    assert qtile.c.screen[1].info()["index"] == 1
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.screen[22].info()
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.screen["foo"].info()
 
 
-@Xephyr(True, ServerConfig())
-def test_items_group(self):
-    g = self.c.group
+@server_config
+def test_items_group(qtile):
+    g = qtile.c.group
     assert g.items("layout") == (True, [0, 1, 2])
 
-    win = self.testWindow("test")
-    wid = self.c.window.info()["id"]
+    win = qtile.testWindow("test")
+    wid = qtile.c.window.info()["id"]
     assert g.items("window") == (True, [wid])
 
     assert g.items("screen") == (True, None)
 
 
-@Xephyr(True, ServerConfig())
-def test_select_group(self):
-    g = self.c.group
+@server_config
+def test_select_group(qtile):
+    g = qtile.c.group
     assert g.layout.info()["group"] == "a"
     assert len(g.layout.info()["stacks"]) == 1
     assert len(g.layout[2].info()["stacks"]) == 3
 
-    assert_raises(libqtile.command.CommandError, self.c.group.window.info)
-    win = self.testWindow("test")
-    wid = self.c.window.info()["id"]
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.group.window.info()
+    win = qtile.testWindow("test")
+    wid = qtile.c.window.info()["id"]
 
     assert g.window.info()["id"] == wid
     assert g.window[wid].info()["id"] == wid
-    assert_raises(libqtile.command.CommandError, g.window["foo"].info)
+    with pytest.raises(libqtile.command.CommandError):
+        g.window["foo"].info()
 
     assert g.screen.info()["index"] == 0
     assert g["b"].screen.info()["index"] == 1
-    assert_raises(libqtile.command.CommandError, g["b"].screen[0].info)
+    with pytest.raises(libqtile.command.CommandError):
+        g["b"].screen[0].info()
 
 
-@Xephyr(True, ServerConfig())
-def test_items_screen(self):
-    s = self.c.screen
+@server_config
+def test_items_screen(qtile):
+    s = qtile.c.screen
     assert s.items("layout") == (True, [0, 1, 2])
 
-    win = self.testWindow("test")
-    wid = self.c.window.info()["id"]
+    win = qtile.testWindow("test")
+    wid = qtile.c.window.info()["id"]
     assert s.items("window") == (True, [wid])
 
     assert s.items("bar") == (False, ["bottom"])
 
 
-@Xephyr(True, ServerConfig())
-def test_select_screen(self):
-    s = self.c.screen
+@server_config
+def test_select_screen(qtile):
+    s = qtile.c.screen
     assert s.layout.info()["group"] == "a"
     assert len(s.layout.info()["stacks"]) == 1
     assert len(s.layout[2].info()["stacks"]) == 3
 
-    assert_raises(libqtile.command.CommandError, self.c.window.info)
-    assert_raises(libqtile.command.CommandError, self.c.window[2].info)
-    win = self.testWindow("test")
-    wid = self.c.window.info()["id"]
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.window.info()
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.window[2].info()
+    win = qtile.testWindow("test")
+    wid = qtile.c.window.info()["id"]
     assert s.window.info()["id"] == wid
     assert s.window[wid].info()["id"] == wid
 
-    assert_raises(libqtile.command.CommandError, s.bar.info)
-    assert_raises(libqtile.command.CommandError, s.bar["top"].info)
+    with pytest.raises(libqtile.command.CommandError):
+        s.bar.info()
+    with pytest.raises(libqtile.command.CommandError):
+        s.bar["top"].info()
     assert s.bar["bottom"].info()["position"] == "bottom"
 
 
-@Xephyr(True, ServerConfig())
-def test_items_bar(self):
-    assert self.c.bar["bottom"].items("screen") == (True, None)
+@server_config
+def test_items_bar(qtile):
+    assert qtile.c.bar["bottom"].items("screen") == (True, None)
 
 
-@Xephyr(True, ServerConfig())
-def test_select_bar(self):
-    assert self.c.screen[1].bar["bottom"].screen.info()["index"] == 1
-    b = self.c.bar
+@server_config
+def test_select_bar(qtile):
+    assert qtile.c.screen[1].bar["bottom"].screen.info()["index"] == 1
+    b = qtile.c.bar
     assert b["bottom"].screen.info()["index"] == 0
-    assert_raises(libqtile.command.CommandError, b.screen.info)
+    with pytest.raises(libqtile.command.CommandError):
+        b.screen.info()
 
 
-@Xephyr(True, ServerConfig())
-def test_items_layout(self):
-    assert self.c.layout.items("screen") == (True, None)
-    assert self.c.layout.items("group") == (True, None)
+@server_config
+def test_items_layout(qtile):
+    assert qtile.c.layout.items("screen") == (True, None)
+    assert qtile.c.layout.items("group") == (True, None)
 
 
-@Xephyr(True, ServerConfig())
-def test_select_layout(self):
-    assert self.c.layout.screen.info()["index"] == 0
-    assert_raises(libqtile.command.CommandError, self.c.layout.screen[0].info)
+@server_config
+def test_select_layout(qtile):
+    assert qtile.c.layout.screen.info()["index"] == 0
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.layout.screen[0].info()
 
-    assert self.c.layout.group.info()["name"] == "a"
-    assert_raises(libqtile.command.CommandError, self.c.layout.group["a"].info)
+    assert qtile.c.layout.group.info()["name"] == "a"
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.layout.group["a"].info()
 
 
-@Xephyr(True, ServerConfig())
-def test_items_window(self):
-    win = self.testWindow("test")
-    wid = self.c.window.info()["id"]
+@server_config
+def test_items_window(qtile):
+    win = qtile.testWindow("test")
+    wid = qtile.c.window.info()["id"]
 
-    assert self.c.window.items("group") == (True, None)
-    assert self.c.window.items("layout") == (True, [0, 1, 2])
-    assert self.c.window.items("screen") == (True, None)
+    assert qtile.c.window.items("group") == (True, None)
+    assert qtile.c.window.items("layout") == (True, [0, 1, 2])
+    assert qtile.c.window.items("screen") == (True, None)
 
 
-@Xephyr(True, ServerConfig())
-def test_select_window(self):
-    win = self.testWindow("test")
-    wid = self.c.window.info()["id"]
+@server_config
+def test_select_window(qtile):
+    win = qtile.testWindow("test")
+    wid = qtile.c.window.info()["id"]
 
-    assert self.c.window.group.info()["name"] == "a"
-    assert_raises(libqtile.command.CommandError, self.c.window.group["a"].info)
+    assert qtile.c.window.group.info()["name"] == "a"
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.window.group["a"].info()
 
-    assert len(self.c.window.layout.info()["stacks"]) == 1
-    assert len(self.c.window.layout[1].info()["stacks"]) == 2
+    assert len(qtile.c.window.layout.info()["stacks"]) == 1
+    assert len(qtile.c.window.layout[1].info()["stacks"]) == 2
 
-    assert self.c.window.screen.info()["index"] == 0
-    assert_raises(libqtile.command.CommandError, self.c.window.screen[0].info)
+    assert qtile.c.window.screen.info()["index"] == 0
+    with pytest.raises(libqtile.command.CommandError):
+        qtile.c.window.screen[0].info()
 
 
-@Xephyr(True, ServerConfig())
-def test_items_widget(self):
-    assert self.c.widget["one"].items("bar") == (True, None)
+@server_config
+def test_items_widget(qtile):
+    assert qtile.c.widget["one"].items("bar") == (True, None)
 
 
-@Xephyr(True, ServerConfig())
-def test_select_widget(self):
-    w = self.c.widget["one"]
+@server_config
+def test_select_widget(qtile):
+    w = qtile.c.widget["one"]
     assert w.bar.info()["position"] == "bottom"
-    assert_raises(libqtile.command.CommandError, w.bar["bottom"].info)
+    with pytest.raises(libqtile.command.CommandError):
+        w.bar["bottom"].info()
diff --git a/test/test_config.py b/test/test_config.py
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -20,21 +20,25 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import os
+import pytest
+
 from libqtile import confreader
 from libqtile import config, utils
-from nose.tools import raises, assert_raises
 
-import os
 tests_dir = os.path.dirname(os.path.realpath(__file__))
 
-@raises(confreader.ConfigError)
+
 def test_syntaxerr():
-    confreader.File(os.path.join(tests_dir, "configs", "syntaxerr.py"))
+    with pytest.raises(confreader.ConfigError):
+        confreader.File(os.path.join(tests_dir, "configs", "syntaxerr.py"))
+
 
 def test_basic():
     f = confreader.File(os.path.join(tests_dir, "configs", "basic.py"))
     assert f.keys
 
+
 def test_falls_back():
     f = confreader.File(os.path.join(tests_dir, "configs", "basic.py"))
 
@@ -43,6 +47,7 @@ def test_falls_back():
     # someone changes it down the road.
     assert hasattr(f, "follow_mouse_focus")
 
+
 def test_ezkey():
     cmd = lambda x: None
 
@@ -57,18 +62,19 @@ def test_ezkey():
     assert key.key == 'Tab'
     assert key.commands == (cmd,)
 
-    with assert_raises(utils.QtileError):
+    with pytest.raises(utils.QtileError):
         config.EzKey('M--', cmd)
 
-    with assert_raises(utils.QtileError):
+    with pytest.raises(utils.QtileError):
         config.EzKey('Z-Z-z', cmd)
 
-    with assert_raises(utils.QtileError):
+    with pytest.raises(utils.QtileError):
         config.EzKey('asdf', cmd)
 
-    with assert_raises(utils.QtileError):
+    with pytest.raises(utils.QtileError):
         config.EzKey('M-a-A', cmd)
 
+
 def test_ezclick_ezdrag():
     cmd = lambda x: None
 
diff --git a/test/test_fakescreen.py b/test/test_fakescreen.py
--- a/test/test_fakescreen.py
+++ b/test/test_fakescreen.py
@@ -23,11 +23,12 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 import libqtile.manager
 import libqtile.config
 from libqtile import layout, bar, widget
 from libqtile.config import Screen
-from .utils import Xephyr
 
 LEFT_ALT = 'mod1'
 WINDOWS = 'mod4'
@@ -147,242 +148,249 @@ class FakeScreenConfig(object):
     screens = fake_screens
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_basic(self):
-    self.testWindow("zero")
-    assert self.c.layout.info()["clients"] == ["zero"]
-    assert self.c.screen.info() == {
+xephyr_config = {
+    "xinerama": False,
+    "two_screens": False,
+    "width": 900,
+    "height": 980
+}
+fakescreen_config = pytest.mark.parametrize("xephyr, qtile", [(xephyr_config, FakeScreenConfig)], indirect=True)
+
+
+@fakescreen_config
+def test_basic(qtile):
+    qtile.testWindow("zero")
+    assert qtile.c.layout.info()["clients"] == ["zero"]
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 0, 'index': 0, 'width': 600, 'height': 480}
-    self.c.to_screen(1)
-    self.testWindow("one")
-    assert self.c.layout.info()["clients"] == ["one"]
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(1)
+    qtile.testWindow("one")
+    assert qtile.c.layout.info()["clients"] == ["one"]
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 600, 'index': 1, 'width': 300, 'height': 580}
-    self.c.to_screen(2)
-    self.testXeyes()
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(2)
+    qtile.testXeyes()
+    assert qtile.c.screen.info() == {
         'y': 480, 'x': 0, 'index': 2, 'width': 500, 'height': 400}
-    self.c.to_screen(3)
-    self.testXclock()
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(3)
+    qtile.testXclock()
+    assert qtile.c.screen.info() == {
         'y': 580, 'x': 500, 'index': 3, 'width': 400, 'height': 400}
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_gaps(self):
-    g = self.c.screens()[0]["gaps"]
+@fakescreen_config
+def test_gaps(qtile):
+    g = qtile.c.screens()[0]["gaps"]
     assert g["bottom"] == (0, 456, 600, 24)
     assert g["left"] == (0, 0, 16, 456)
     assert g["right"] == (580, 0, 20, 456)
-    g = self.c.screens()[1]["gaps"]
+    g = qtile.c.screens()[1]["gaps"]
     assert g["top"] == (600, 0, 300, 30)
     assert g["bottom"] == (600, 556, 300, 24)
     assert g["left"] == (600, 30, 12, 526)
-    g = self.c.screens()[2]["gaps"]
+    g = qtile.c.screens()[2]["gaps"]
     assert g["top"] == (0, 480, 500, 30)
     assert g["bottom"] == (0, 864, 500, 16)
     assert g["right"] == (460, 510, 40, 354)
-    g = self.c.screens()[3]["gaps"]
+    g = qtile.c.screens()[3]["gaps"]
     assert g["top"] == (500, 580, 400, 30)
     assert g["left"] == (500, 610, 20, 370)
     assert g["right"] == (876, 610, 24, 370)
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_maximize_with_move_to_screen(self):
-    """
-    Ensure that maximize respects bars
-    """
-    self.testXclock()
-    self.c.window.toggle_maximize()
-    assert self.c.window.info()['width'] == 564
-    assert self.c.window.info()['height'] == 456
-    assert self.c.window.info()['x'] == 16
-    assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['group'] == 'a'
+@fakescreen_config
+def test_maximize_with_move_to_screen(qtile):
+    """Ensure that maximize respects bars"""
+    qtile.testXclock()
+    qtile.c.window.toggle_maximize()
+    assert qtile.c.window.info()['width'] == 564
+    assert qtile.c.window.info()['height'] == 456
+    assert qtile.c.window.info()['x'] == 16
+    assert qtile.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['group'] == 'a'
 
     # go to second screen
-    self.c.to_screen(1)
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(1)
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 600, 'index': 1, 'width': 300, 'height': 580}
-    assert self.c.group.info()['name'] == 'b'
-    self.c.group['a'].toscreen()
+    assert qtile.c.group.info()['name'] == 'b'
+    qtile.c.group['a'].toscreen()
 
-    assert self.c.window.info()['width'] == 288
-    assert self.c.window.info()['height'] == 526
-    assert self.c.window.info()['x'] == 612
-    assert self.c.window.info()['y'] == 30
-    assert self.c.window.info()['group'] == 'a'
+    assert qtile.c.window.info()['width'] == 288
+    assert qtile.c.window.info()['height'] == 526
+    assert qtile.c.window.info()['x'] == 612
+    assert qtile.c.window.info()['y'] == 30
+    assert qtile.c.window.info()['group'] == 'a'
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_float_first_on_second_screen(self):
-    self.c.to_screen(1)
-    assert self.c.screen.info() == {
+@fakescreen_config
+def test_float_first_on_second_screen(qtile):
+    qtile.c.to_screen(1)
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 600, 'index': 1, 'width': 300, 'height': 580}
 
-    self.testXclock()
+    qtile.testXclock()
     # I don't know where y=30, x=12 comes from...
-    assert self.c.window.info()['float_info'] == {
+    assert qtile.c.window.info()['float_info'] == {
         'y': 30, 'x': 12, 'width': 164, 'height': 164
     }
 
-    self.c.window.toggle_floating()
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
+    qtile.c.window.toggle_floating()
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
 
-    assert self.c.window.info()['x'] == 612
-    assert self.c.window.info()['y'] == 30
-    assert self.c.window.info()['group'] == 'b'
-    assert self.c.window.info()['float_info'] == {
+    assert qtile.c.window.info()['x'] == 612
+    assert qtile.c.window.info()['y'] == 30
+    assert qtile.c.window.info()['group'] == 'b'
+    assert qtile.c.window.info()['float_info'] == {
         'y': 30, 'x': 12, 'width': 164, 'height': 164
     }
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_float_change_screens(self):
-    #add some eyes, and float clock
-    self.testXeyes()
-    self.testXclock()
-    self.c.window.toggle_floating()
-    assert set(self.c.group.info()['windows']) == set(('xeyes', 'xclock'))
-    assert self.c.group.info()['floating_info']['clients'] == ['xclock']
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
+@fakescreen_config
+def test_float_change_screens(qtile):
+    # add some eyes, and float clock
+    qtile.testXeyes()
+    qtile.testXclock()
+    qtile.c.window.toggle_floating()
+    assert set(qtile.c.group.info()['windows']) == set(('xeyes', 'xclock'))
+    assert qtile.c.group.info()['floating_info']['clients'] == ['xclock']
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
     # 16 is given by the left gap width
-    assert self.c.window.info()['x'] == 16
-    assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['group'] == 'a'
+    assert qtile.c.window.info()['x'] == 16
+    assert qtile.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['group'] == 'a'
 
-    #put on group b
-    assert self.c.screen.info() == {
+    # put on group b
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 0, 'index': 0, 'width': 600, 'height': 480}
-    assert self.c.group.info()['name'] == 'a'
-    self.c.to_screen(1)
-    assert self.c.group.info()['name'] == 'b'
-    assert self.c.screen.info() == {
+    assert qtile.c.group.info()['name'] == 'a'
+    qtile.c.to_screen(1)
+    assert qtile.c.group.info()['name'] == 'b'
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 600, 'index': 1, 'width': 300, 'height': 580}
-    self.c.group['a'].toscreen()
-    assert self.c.group.info()['name'] == 'a'
-    assert set(self.c.group.info()['windows']) == set(('xeyes', 'xclock'))
-    assert self.c.window.info()['name'] == 'xclock'
+    qtile.c.group['a'].toscreen()
+    assert qtile.c.group.info()['name'] == 'a'
+    assert set(qtile.c.group.info()['windows']) == set(('xeyes', 'xclock'))
+    assert qtile.c.window.info()['name'] == 'xclock'
     # width/height unchanged
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
     # x is shifted by 600, y is shifted by 0
-    assert self.c.window.info()['x'] == 616
-    assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['group'] == 'a'
-    assert self.c.group.info()['floating_info']['clients'] == ['xclock']
+    assert qtile.c.window.info()['x'] == 616
+    assert qtile.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['group'] == 'a'
+    assert qtile.c.group.info()['floating_info']['clients'] == ['xclock']
 
     # move to screen 3
-    self.c.to_screen(2)
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(2)
+    assert qtile.c.screen.info() == {
         'y': 480, 'x': 0, 'index': 2, 'width': 500, 'height': 400}
-    assert self.c.group.info()['name'] == 'c'
-    self.c.group['a'].toscreen()
-    assert self.c.group.info()['name'] == 'a'
-    assert set(self.c.group.info()['windows']) == set(('xeyes', 'xclock'))
-    assert self.c.window.info()['name'] == 'xclock'
+    assert qtile.c.group.info()['name'] == 'c'
+    qtile.c.group['a'].toscreen()
+    assert qtile.c.group.info()['name'] == 'a'
+    assert set(qtile.c.group.info()['windows']) == set(('xeyes', 'xclock'))
+    assert qtile.c.window.info()['name'] == 'xclock'
     # width/height unchanged
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
     # x is shifted by 0, y is shifted by 480
-    assert self.c.window.info()['x'] == 16
-    assert self.c.window.info()['y'] == 480
+    assert qtile.c.window.info()['x'] == 16
+    assert qtile.c.window.info()['y'] == 480
 
     # now screen 4 for fun
-    self.c.to_screen(3)
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(3)
+    assert qtile.c.screen.info() == {
         'y': 580, 'x': 500, 'index': 3, 'width': 400, 'height': 400}
-    assert self.c.group.info()['name'] == 'd'
-    self.c.group['a'].toscreen()
-    assert self.c.group.info()['name'] == 'a'
-    assert set(self.c.group.info()['windows']) == set(('xeyes', 'xclock'))
-    assert self.c.window.info()['name'] == 'xclock'
+    assert qtile.c.group.info()['name'] == 'd'
+    qtile.c.group['a'].toscreen()
+    assert qtile.c.group.info()['name'] == 'a'
+    assert set(qtile.c.group.info()['windows']) == set(('xeyes', 'xclock'))
+    assert qtile.c.window.info()['name'] == 'xclock'
     # width/height unchanged
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
     # x is shifted by 500, y is shifted by 580
-    assert self.c.window.info()['x'] == 516
-    assert self.c.window.info()['y'] == 580
+    assert qtile.c.window.info()['x'] == 516
+    assert qtile.c.window.info()['y'] == 580
 
     # and back to one
-    self.c.to_screen(0)
-    assert self.c.screen.info() == {
+    qtile.c.to_screen(0)
+    assert qtile.c.screen.info() == {
         'y': 0, 'x': 0, 'index': 0, 'width': 600, 'height': 480}
-    assert self.c.group.info()['name'] == 'b'
-    self.c.group['a'].toscreen()
-    assert self.c.group.info()['name'] == 'a'
-    assert set(self.c.group.info()['windows']) == set(('xeyes', 'xclock'))
-    assert self.c.window.info()['name'] == 'xclock'
+    assert qtile.c.group.info()['name'] == 'b'
+    qtile.c.group['a'].toscreen()
+    assert qtile.c.group.info()['name'] == 'a'
+    assert set(qtile.c.group.info()['windows']) == set(('xeyes', 'xclock'))
+    assert qtile.c.window.info()['name'] == 'xclock'
     # back to the original location
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
-    assert self.c.window.info()['x'] == 16
-    assert self.c.window.info()['y'] == 0
-
-
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_float_outside_edges(self):
-    self.testXclock()
-    self.c.window.toggle_floating()
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['x'] == 16
+    assert qtile.c.window.info()['y'] == 0
+
+
+@fakescreen_config
+def test_float_outside_edges(qtile):
+    qtile.testXclock()
+    qtile.c.window.toggle_floating()
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
     # 16 is given by the left gap width
-    assert self.c.window.info()['x'] == 16
-    assert self.c.window.info()['y'] == 0
+    assert qtile.c.window.info()['x'] == 16
+    assert qtile.c.window.info()['y'] == 0
     # empty because window is floating
-    assert self.c.layout.info() == {
+    assert qtile.c.layout.info() == {
         'clients': [], 'group': 'a', 'name': 'max'}
 
     # move left, but some still on screen 0
-    self.c.window.move_floating(-30, 20, 42, 42)
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
-    assert self.c.window.info()['x'] == -14
-    assert self.c.window.info()['y'] == 20
-    assert self.c.window.info()['group'] == 'a'
+    qtile.c.window.move_floating(-30, 20, 42, 42)
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['x'] == -14
+    assert qtile.c.window.info()['y'] == 20
+    assert qtile.c.window.info()['group'] == 'a'
 
     # move up, but some still on screen 0
-    self.c.window.set_position_floating(-10, -20, 42, 42)
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
-    assert self.c.window.info()['x'] == -10
-    assert self.c.window.info()['y'] == -20
-    assert self.c.window.info()['group'] == 'a'
+    qtile.c.window.set_position_floating(-10, -20, 42, 42)
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['x'] == -10
+    assert qtile.c.window.info()['y'] == -20
+    assert qtile.c.window.info()['group'] == 'a'
 
     # move above a
-    self.c.window.set_position_floating(50, -20, 42, 42)
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
-    assert self.c.window.info()['x'] == 50
-    assert self.c.window.info()['y'] == -20
-    assert self.c.window.info()['group'] == 'a'
+    qtile.c.window.set_position_floating(50, -20, 42, 42)
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['x'] == 50
+    assert qtile.c.window.info()['y'] == -20
+    assert qtile.c.window.info()['group'] == 'a'
 
     # move down so still left, but next to screen c
-    self.c.window.set_position_floating(-10, 520, 42, 42)
-    assert self.c.window.info()['height'] == 164
-    assert self.c.window.info()['x'] == -10
-    assert self.c.window.info()['y'] == 520
-    assert self.c.window.info()['group'] == 'c'
+    qtile.c.window.set_position_floating(-10, 520, 42, 42)
+    assert qtile.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['x'] == -10
+    assert qtile.c.window.info()['y'] == 520
+    assert qtile.c.window.info()['group'] == 'c'
 
     # move above b
-    self.c.window.set_position_floating(700, -10, 42, 42)
-    assert self.c.window.info()['width'] == 164
-    assert self.c.window.info()['height'] == 164
-    assert self.c.window.info()['x'] == 700
-    assert self.c.window.info()['y'] == -10
-    assert self.c.window.info()['group'] == 'b'
+    qtile.c.window.set_position_floating(700, -10, 42, 42)
+    assert qtile.c.window.info()['width'] == 164
+    assert qtile.c.window.info()['height'] == 164
+    assert qtile.c.window.info()['x'] == 700
+    assert qtile.c.window.info()['y'] == -10
+    assert qtile.c.window.info()['group'] == 'b'
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_hammer_tile(self):
+@fakescreen_config
+def test_hammer_tile(qtile):
     # change to tile layout
-    self.c.next_layout()
-    self.c.next_layout()
+    qtile.c.next_layout()
+    qtile.c.next_layout()
     for i in range(7):
-        self.testXclock()
+        qtile.testXclock()
     for i in range(30):
         old_group = (i + 1) % 4
         if old_group == 0:
@@ -394,19 +402,19 @@ def test_hammer_tile(self):
         elif old_group == 3:
             name = 'd'
 
-        self.c.to_screen((i + 1) % 4)
-        self.c.group['a'].toscreen()
-    assert self.c.group['a'].info()['windows'] == [
+        qtile.c.to_screen((i + 1) % 4)
+        qtile.c.group['a'].toscreen()
+    assert qtile.c.group['a'].info()['windows'] == [
         'xclock', 'xclock', 'xclock', 'xclock',
         'xclock', 'xclock', 'xclock']
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_hammer_ratio_tile(self):
+@fakescreen_config
+def test_hammer_ratio_tile(qtile):
     # change to ratio tile layout
-    self.c.next_layout()
+    qtile.c.next_layout()
     for i in range(7):
-        self.testXclock()
+        qtile.testXclock()
     for i in range(30):
         old_group = (i + 1) % 4
         if old_group == 0:
@@ -418,28 +426,28 @@ def test_hammer_ratio_tile(self):
         elif old_group == 3:
             name = 'd'
 
-        self.c.to_screen((i + 1) % 4)
-        self.c.group['a'].toscreen()
-    assert self.c.group['a'].info()['windows'] == [
+        qtile.c.to_screen((i + 1) % 4)
+        qtile.c.group['a'].toscreen()
+    assert qtile.c.group['a'].info()['windows'] == [
         'xclock', 'xclock', 'xclock', 'xclock',
         'xclock', 'xclock', 'xclock']
 
 
-@Xephyr(False, FakeScreenConfig(), two_screens=False, width=900, height=980)
-def test_ratio_to_fourth_screen(self):
+@fakescreen_config
+def test_ratio_to_fourth_screen(qtile):
     # change to ratio tile layout
-    self.c.next_layout()
+    qtile.c.next_layout()
     for i in range(7):
-        self.testXclock()
-    self.c.to_screen(1)
-    self.c.group['a'].toscreen()
-    assert self.c.group['a'].info()['windows'] == [
+        qtile.testXclock()
+    qtile.c.to_screen(1)
+    qtile.c.group['a'].toscreen()
+    assert qtile.c.group['a'].info()['windows'] == [
         'xclock', 'xclock', 'xclock', 'xclock',
         'xclock', 'xclock', 'xclock']
 
     # now move to 4th, fails...
-    self.c.to_screen(3)
-    self.c.group['a'].toscreen()
-    assert self.c.group['a'].info()['windows'] == [
+    qtile.c.to_screen(3)
+    qtile.c.group['a'].toscreen()
+    assert qtile.c.group['a'].info()['windows'] == [
         'xclock', 'xclock', 'xclock', 'xclock',
         'xclock', 'xclock', 'xclock']
diff --git a/test/test_hook.py b/test/test_hook.py
--- a/test/test_hook.py
+++ b/test/test_hook.py
@@ -21,6 +21,7 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
 from multiprocessing import Value
 
 import libqtile.log_utils
@@ -28,15 +29,14 @@
 import libqtile.utils
 import libqtile.hook
 import logging
-from nose.tools import with_setup, raises
 
-from .utils import Xephyr
-from .utils import BareConfig
+from .conftest import BareConfig
 
 # TODO: more tests required.
 # 1. Check all hooks that can be fired
 
-class TestCall(object):
+
+class Call(object):
     def __init__(self, val):
         self.val = val
 
@@ -44,7 +44,8 @@ def __call__(self, val):
         self.val = val
 
 
-def setup():
+@pytest.yield_fixture
+def hook_fixture():
     class Dummy(object):
         pass
 
@@ -52,36 +53,36 @@ class Dummy(object):
     libqtile.log_utils.init_log(logging.CRITICAL)
     libqtile.hook.init(dummy)
 
+    yield
 
-def teardown():
     libqtile.hook.clear()
 
 
-@raises(libqtile.utils.QtileError)
 def test_cannot_fire_unknown_event():
-    libqtile.hook.fire("unknown")
+    with pytest.raises(libqtile.utils.QtileError):
+        libqtile.hook.fire("unknown")
 
 
-@with_setup(setup, teardown)
+@pytest.mark.usefixtures("hook_fixture")
 def test_hook_calls_subscriber():
-    test = TestCall(0)
+    test = Call(0)
     libqtile.manager.hook.subscribe.group_window_add(test)
     libqtile.manager.hook.fire("group_window_add", 8)
     assert test.val == 8
 
 
-@with_setup(setup, teardown)
+@pytest.mark.usefixtures("hook_fixture")
 def test_subscribers_can_be_added_removed():
-    test = TestCall(0)
+    test = Call(0)
     libqtile.manager.hook.subscribe.group_window_add(test)
     assert libqtile.manager.hook.subscriptions
     libqtile.manager.hook.clear()
     assert not libqtile.manager.hook.subscriptions
 
 
-@with_setup(setup, teardown)
+@pytest.mark.usefixtures("hook_fixture")
 def test_can_unsubscribe_from_hook():
-    test = TestCall(0)
+    test = Call(0)
 
     libqtile.manager.hook.subscribe.group_window_add(test)
     libqtile.manager.hook.fire("group_window_add", 3)
@@ -91,8 +92,10 @@ def test_can_unsubscribe_from_hook():
     libqtile.manager.hook.fire("group_window_add", 4)
     assert test.val == 3
 
-@Xephyr(True, BareConfig(), False)
-def test_can_subscribe_to_startup_hooks(self):
+
+def test_can_subscribe_to_startup_hooks(qtile_nospawn):
+    config = BareConfig
+    self = qtile_nospawn
 
     self.startup_once_calls = Value('i', 0)
     self.startup_calls = Value('i', 0)
@@ -111,7 +114,7 @@ def inc_startup_complete_calls():
     libqtile.manager.hook.subscribe.startup(inc_startup_calls)
     libqtile.manager.hook.subscribe.startup_complete(inc_startup_complete_calls)
 
-    self.startQtile(self.config)
+    self.start(config)
     self.start_qtile = True
     assert self.startup_once_calls.value == 1
     assert self.startup_calls.value == 1
diff --git a/test/test_manager.py b/test/test_manager.py
--- a/test/test_manager.py
+++ b/test/test_manager.py
@@ -25,8 +25,10 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-import time
+import pytest
 import subprocess
+import time
+
 import libqtile
 import libqtile.layout
 import libqtile.bar
@@ -37,15 +39,10 @@
 import libqtile.hook
 import libqtile.confreader
 
-from nose.tools import assert_raises
-from nose.plugins.attrib import attr
-
-from . import utils
-from .utils import Xephyr
-from .utils import BareConfig
+from .conftest import whereis, BareConfig, no_xinerama
 
 
-class TestConfig(object):
+class ManagerConfig(object):
     auto_fullscreen = True
     groups = [
         libqtile.config.Group("a"),
@@ -54,11 +51,11 @@ class TestConfig(object):
         libqtile.config.Group("d")
     ]
     layouts = [
-                libqtile.layout.stack.Stack(num_stacks=1),
-                libqtile.layout.stack.Stack(num_stacks=2),
-                libqtile.layout.tile.Tile(ratio=0.5),
-                libqtile.layout.max.Max()
-            ]
+        libqtile.layout.stack.Stack(num_stacks=1),
+        libqtile.layout.stack.Stack(num_stacks=2),
+        libqtile.layout.tile.Tile(ratio=0.5),
+        libqtile.layout.max.Max()
+    ]
     floating_layout = libqtile.layout.floating.Floating(
         float_rules=[dict(wmclass="xclock")])
     keys = [
@@ -75,45 +72,52 @@ class TestConfig(object):
     ]
     mouse = []
     screens = [libqtile.config.Screen(
-            bottom=libqtile.bar.Bar(
-                        [
-                            libqtile.widget.GroupBox(),
-                        ],
-                        20
-                    ),
+        bottom=libqtile.bar.Bar(
+            [
+                libqtile.widget.GroupBox(),
+            ],
+            20
+        ),
     )]
     main = None
     follow_mouse_focus = True
 
 
-@Xephyr(True, TestConfig())
-def test_screen_dim(self):
-    #self.c.restart()
-    self.testXclock()
-    assert self.c.screen.info()["index"] == 0
-    assert self.c.screen.info()["x"] == 0
-    assert self.c.screen.info()["width"] == 800
-    assert self.c.group.info()["name"] == 'a'
-    assert self.c.group.info()["focus"] == 'xclock'
+manager_config = pytest.mark.parametrize("qtile", [ManagerConfig], indirect=True)
 
-    self.c.to_screen(1)
-    self.testXeyes()
-    assert self.c.screen.info()["index"] == 1
-    assert self.c.screen.info()["x"] == 800
-    assert self.c.screen.info()["width"] == 640
-    assert self.c.group.info()["name"] == 'b'
-    assert self.c.group.info()["focus"] == 'xeyes'
 
-    self.c.to_screen(0)
-    assert self.c.screen.info()["index"] == 0
-    assert self.c.screen.info()["x"] == 0
-    assert self.c.screen.info()["width"] == 800
-    assert self.c.group.info()["name"] == 'a'
-    assert self.c.group.info()["focus"] == 'xclock'
+@manager_config
+def test_screen_dim(qtile):
+    # self.c.restart()
+
+    qtile.testXclock()
+    assert qtile.c.screen.info()["index"] == 0
+    assert qtile.c.screen.info()["x"] == 0
+    assert qtile.c.screen.info()["width"] == 800
+    assert qtile.c.group.info()["name"] == 'a'
+    assert qtile.c.group.info()["focus"] == 'xclock'
+
+    qtile.c.to_screen(1)
+    qtile.testXeyes()
+    assert qtile.c.screen.info()["index"] == 1
+    assert qtile.c.screen.info()["x"] == 800
+    assert qtile.c.screen.info()["width"] == 640
+    assert qtile.c.group.info()["name"] == 'b'
+    assert qtile.c.group.info()["focus"] == 'xeyes'
 
+    qtile.c.to_screen(0)
+    assert qtile.c.screen.info()["index"] == 0
+    assert qtile.c.screen.info()["x"] == 0
+    assert qtile.c.screen.info()["width"] == 800
+    assert qtile.c.group.info()["name"] == 'a'
+    assert qtile.c.group.info()["focus"] == 'xclock'
+
+
+@pytest.mark.parametrize("xephyr", [{"xoffset": 0}], indirect=True)
+@manager_config
+def test_clone_dim(qtile):
+    self = qtile
 
-@Xephyr(True, TestConfig(), xoffset=0)
-def test_clone_dim(self):
     self.testXclock()
     assert self.c.screen.info()["index"] == 0
     assert self.c.screen.info()["x"] == 0
@@ -124,8 +128,10 @@ def test_clone_dim(self):
     assert len(self.c.screens()) == 1
 
 
-@Xephyr(True, TestConfig())
-def test_to_screen(self):
+@manager_config
+def test_to_screen(qtile):
+    self = qtile
+
     assert self.c.screen.info()["index"] == 0
     self.c.to_screen(1)
     assert self.c.screen.info()["index"] == 1
@@ -148,24 +154,28 @@ def test_to_screen(self):
     assert self.c.window.info()["name"] == "one"
 
 
-@Xephyr(True, TestConfig())
-def test_togroup(self):
+@manager_config
+def test_togroup(qtile):
+    self = qtile
+
     self.testWindow("one")
-    assert_raises(libqtile.command.CommandError,
-                  self.c.window.togroup, "nonexistent")
+    with pytest.raises(libqtile.command.CommandError):
+        self.c.window.togroup("nonexistent")
     assert self.c.groups()["a"]["focus"] == "one"
     self.c.window.togroup("a")
     assert self.c.groups()["a"]["focus"] == "one"
     self.c.window.togroup("b")
     assert self.c.groups()["b"]["focus"] == "one"
-    assert self.c.groups()["a"]["focus"] == None
+    assert self.c.groups()["a"]["focus"] is None
     self.c.to_screen(1)
     self.c.window.togroup("c")
     assert self.c.groups()["c"]["focus"] == "one"
 
 
-@Xephyr(True, TestConfig())
-def test_resize(self):
+@manager_config
+def test_resize(qtile):
+    self = qtile
+
     self.c.screen[0].resize(x=10, y=10, w=100, h=100)
     for _ in range(10):
         time.sleep(0.1)
@@ -178,20 +188,24 @@ def test_resize(self):
     assert d["x"] == d["y"] == 10
 
 
-@Xephyr(False, BareConfig())
-def test_minimal(self):
-    assert self.c.status() == "OK"
+@no_xinerama
+def test_minimal(qtile):
+    assert qtile.c.status() == "OK"
 
 
-@Xephyr(False, TestConfig())
-def test_events(self):
-    assert self.c.status() == "OK"
+@manager_config
+@no_xinerama
+def test_events(qtile):
+    assert qtile.c.status() == "OK"
 
 
 # FIXME: failing test disabled. For some reason we don't seem
 # to have a keymap in Xnest or Xephyr 99% of the time.
-@Xephyr(False, TestConfig())
-def test_keypress(self):
+@manager_config
+@no_xinerama
+def test_keypress(qtile):
+    self = qtile
+
     self.testWindow("one")
     self.testWindow("two")
     v = self.c.simulate_keypress(["unknown"], "j")
@@ -201,33 +215,40 @@ def test_keypress(self):
     assert self.c.groups()["a"]["focus"] == "one"
 
 
-@Xephyr(False, TestConfig())
-def test_spawn(self):
+@manager_config
+@no_xinerama
+def test_spawn(qtile):
     # Spawn something with a pid greater than init's
-    assert int(self.c.spawn("true")) > 1
+    assert int(qtile.c.spawn("true")) > 1
 
 
-@Xephyr(False, TestConfig())
-def test_spawn_list(self):
+@manager_config
+@no_xinerama
+def test_spawn_list(qtile):
     # Spawn something with a pid greater than init's
-    assert int(self.c.spawn(["echo", "true"])) > 1
+    assert int(qtile.c.spawn(["echo", "true"])) > 1
 
 
-@Xephyr(False, TestConfig())
-def test_kill(self):
-    self.testWindow("one")
-    self.testwindows = []
-    self.c.window[self.c.window.info()["id"]].kill()
-    self.c.sync()
+@manager_config
+@no_xinerama
+def test_kill_window(qtile):
+    qtile.testWindow("one")
+    qtile.testwindows = []
+    qtile.c.window[qtile.c.window.info()["id"]].kill()
+    qtile.c.sync()
     for _ in range(20):
         time.sleep(0.1)
-        if len(self.c.windows()) == 0:
+        if len(qtile.c.windows()) == 0:
             break
     else:
         raise AssertionError("Window did not die...")
 
-@Xephyr(False, TestConfig())
-def test_kill_other(self):
+
+@manager_config
+@no_xinerama
+def test_kill_other(qtile):
+    self = qtile
+
     self.c.group.setlayout("tile")
     one = self.testWindow("one")
     assert self.c.window.info()["width"] == 798
@@ -238,7 +259,7 @@ def test_kill_other(self):
     assert self.c.window.info()["height"] == 578
     assert len(self.c.windows()) == 2
 
-    self.kill(one)
+    self.kill_window(one)
     for _ in range(10):
         time.sleep(0.1)
         if len(self.c.windows()) == 1:
@@ -250,15 +271,22 @@ def test_kill_other(self):
     assert self.c.window.info()["width"] == 798
     assert self.c.window.info()["height"] == 578
 
-@Xephyr(False, TestConfig())
-def test_regression_groupswitch(self):
+
+@manager_config
+@no_xinerama
+def test_regression_groupswitch(qtile):
+    self = qtile
+
     self.c.group["c"].toscreen()
     self.c.group["d"].toscreen()
-    assert self.c.groups()["c"]["screen"] == None
+    assert self.c.groups()["c"]["screen"] is None
+
 
+@manager_config
+@no_xinerama
+def test_next_layout(qtile):
+    self = qtile
 
-@Xephyr(False, TestConfig())
-def test_next_layout(self):
     self.testWindow("one")
     self.testWindow("two")
     assert len(self.c.layout.info()["stacks"]) == 1
@@ -270,40 +298,54 @@ def test_next_layout(self):
     assert len(self.c.layout.info()["stacks"]) == 1
 
 
-@Xephyr(False, TestConfig())
-def test_setlayout(self):
+@manager_config
+@no_xinerama
+def test_setlayout(qtile):
+    self = qtile
+
     assert not self.c.layout.info()["name"] == "max"
     self.c.group.setlayout("max")
     assert self.c.layout.info()["name"] == "max"
 
 
-@Xephyr(False, TestConfig())
-def test_adddelgroup(self):
+@manager_config
+@no_xinerama
+def test_adddelgroup(qtile):
+    self = qtile
+
     self.testWindow("one")
     self.c.addgroup("dummygroup")
     self.c.addgroup("testgroup")
     assert "testgroup" in self.c.groups().keys()
     self.c.window.togroup("testgroup")
     self.c.delgroup("testgroup")
-    assert not "testgroup" in self.c.groups().keys()
+    assert "testgroup" not in self.c.groups().keys()
     # Assert that the test window is still a member of some group.
     assert sum(len(i["windows"]) for i in self.c.groups().values())
+
     for i in list(self.c.groups().keys())[:-1]:
         self.c.delgroup(i)
-    assert_raises(libqtile.command.CommandException,
-                  self.c.delgroup, list(self.c.groups().keys())[0])
+    with pytest.raises(libqtile.command.CommandException):
+        self.c.delgroup(list(self.c.groups().keys())[0])
+
 
+@manager_config
+@no_xinerama
+def test_delgroup(qtile):
+    self = qtile
 
-@Xephyr(False, TestConfig())
-def test_delgroup(self):
     self.testWindow("one")
     for i in ['a', 'd', 'c']:
         self.c.delgroup(i)
-    assert_raises(libqtile.command.CommandException, self.c.delgroup, 'b')
+    with pytest.raises(libqtile.command.CommandException):
+        self.c.delgroup('b')
 
 
-@Xephyr(False, TestConfig())
-def test_nextprevgroup(self):
+@manager_config
+@no_xinerama
+def test_nextprevgroup(qtile):
+    self = qtile
+
     start = self.c.group.info()["name"]
     ret = self.c.screen.next_group()
     assert self.c.group.info()["name"] != start
@@ -312,8 +354,11 @@ def test_nextprevgroup(self):
     assert self.c.group.info()["name"] == start
 
 
-@Xephyr(False, TestConfig())
-def test_toggle_group(self):
+@manager_config
+@no_xinerama
+def test_toggle_group(qtile):
+    self = qtile
+
     self.c.group["a"].toscreen()
     self.c.group["b"].toscreen()
     self.c.screen.toggle_group("c")
@@ -324,34 +369,49 @@ def test_toggle_group(self):
     assert self.c.group.info()["name"] == "c"
 
 
-@Xephyr(False, TestConfig())
-def test_inspect_xeyes(self):
+@manager_config
+@no_xinerama
+def test_inspect_xeyes(qtile):
+    self = qtile
+
     self.testXeyes()
     assert self.c.window.inspect()
 
 
-@Xephyr(False, TestConfig())
-def test_inspect_xterm(self):
+@manager_config
+@no_xinerama
+def test_inspect_xterm(qtile):
+    self = qtile
+
     self.testXterm()
     assert self.c.window.inspect()["wm_class"]
 
 
-@Xephyr(False, TestConfig())
-def test_static(self):
+@manager_config
+@no_xinerama
+def test_static(qtile):
+    self = qtile
+
     self.testXeyes()
     self.testWindow("one")
     self.c.window[self.c.window.info()["id"]].static(0, 0, 0, 100, 100)
 
 
-@Xephyr(False, TestConfig())
-def test_match(self):
+@manager_config
+@no_xinerama
+def test_match(qtile):
+    self = qtile
+
     self.testXeyes()
     assert self.c.window.match(wname="xeyes")
     assert not self.c.window.match(wname="nonexistent")
 
 
-@Xephyr(False, TestConfig())
-def test_default_float(self):
+@manager_config
+@no_xinerama
+def test_default_float(qtile):
+    self = qtile
+
     # change to 2 col stack
     self.c.next_layout()
     assert len(self.c.layout.info()["stacks"]) == 2
@@ -362,21 +422,24 @@ def test_default_float(self):
     assert self.c.window.info()['height'] == 164
     assert self.c.window.info()['x'] == 0
     assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['floating'] == True
+    assert self.c.window.info()['floating'] is True
 
     self.c.window.move_floating(10, 20, 42, 42)
     assert self.c.window.info()['width'] == 164
     assert self.c.window.info()['height'] == 164
     assert self.c.window.info()['x'] == 10
     assert self.c.window.info()['y'] == 20
-    assert self.c.window.info()['floating'] == True
+    assert self.c.window.info()['floating'] is True
 
 
-@Xephyr(False, TestConfig())
-def test_last_float_size(self):
+@manager_config
+@no_xinerama
+def test_last_float_size(qtile):
     """
     When you re-float something it would be preferable to have it use the previous float size
     """
+    self = qtile
+
     self.testXeyes()
     assert self.c.window.info()['name'] == 'xeyes'
     assert self.c.window.info()['width'] == 798
@@ -407,8 +470,11 @@ def test_last_float_size(self):
     assert self.c.window.info()['height'] == 90
 
 
-@Xephyr(False, TestConfig())
-def test_float_max_min_combo(self):
+@manager_config
+@no_xinerama
+def test_float_max_min_combo(qtile):
+    self = qtile
+
     # change to 2 col stack
     self.c.next_layout()
     assert len(self.c.layout.info()["stacks"]) == 2
@@ -420,11 +486,11 @@ def test_float_max_min_combo(self):
     assert self.c.window.info()['height'] == 578
     assert self.c.window.info()['x'] == 400
     assert self.c.window.info()['y'] == 0
-    assert self.c.window.info()['floating'] == False
+    assert self.c.window.info()['floating'] is False
 
     self.c.window.toggle_maximize()
-    assert self.c.window.info()['floating'] == True
-    assert self.c.window.info()['maximized'] == True
+    assert self.c.window.info()['floating'] is True
+    assert self.c.window.info()['maximized'] is True
     assert self.c.window.info()['width'] == 800
     assert self.c.window.info()['height'] == 580
     assert self.c.window.info()['x'] == 0
@@ -432,8 +498,8 @@ def test_float_max_min_combo(self):
 
     self.c.window.toggle_minimize()
     assert self.c.group.info()['focus'] == 'xeyes'
-    assert self.c.window.info()['floating'] == True
-    assert self.c.window.info()['minimized'] == True
+    assert self.c.window.info()['floating'] is True
+    assert self.c.window.info()['minimized'] is True
     assert self.c.window.info()['width'] == 800
     assert self.c.window.info()['height'] == 580
     assert self.c.window.info()['x'] == 0
@@ -441,17 +507,20 @@ def test_float_max_min_combo(self):
 
     self.c.window.toggle_floating()
     assert self.c.group.info()['focus'] == 'xeyes'
-    assert self.c.window.info()['floating'] == False
-    assert self.c.window.info()['minimized'] == False
-    assert self.c.window.info()['maximized'] == False
+    assert self.c.window.info()['floating'] is False
+    assert self.c.window.info()['minimized'] is False
+    assert self.c.window.info()['maximized'] is False
     assert self.c.window.info()['width'] == 398
     assert self.c.window.info()['height'] == 578
     assert self.c.window.info()['x'] == 400
     assert self.c.window.info()['y'] == 0
 
 
-@Xephyr(False, TestConfig())
-def test_toggle_fullscreen(self):
+@manager_config
+@no_xinerama
+def test_toggle_fullscreen(qtile):
+    self = qtile
+
     # change to 2 col stack
     self.c.next_layout()
     assert len(self.c.layout.info()["stacks"]) == 2
@@ -467,26 +536,29 @@ def test_toggle_fullscreen(self):
     assert self.c.window.info()['y'] == 0
 
     self.c.window.toggle_fullscreen()
-    assert self.c.window.info()['floating'] == True
-    assert self.c.window.info()['maximized'] == False
-    assert self.c.window.info()['fullscreen'] == True
+    assert self.c.window.info()['floating'] is True
+    assert self.c.window.info()['maximized'] is False
+    assert self.c.window.info()['fullscreen'] is True
     assert self.c.window.info()['width'] == 800
     assert self.c.window.info()['height'] == 600
     assert self.c.window.info()['x'] == 0
     assert self.c.window.info()['y'] == 0
 
     self.c.window.toggle_fullscreen()
-    assert self.c.window.info()['floating'] == False
-    assert self.c.window.info()['maximized'] == False
-    assert self.c.window.info()['fullscreen'] == False
+    assert self.c.window.info()['floating'] is False
+    assert self.c.window.info()['maximized'] is False
+    assert self.c.window.info()['fullscreen'] is False
     assert self.c.window.info()['width'] == 398
     assert self.c.window.info()['height'] == 578
     assert self.c.window.info()['x'] == 400
     assert self.c.window.info()['y'] == 0
 
 
-@Xephyr(False, TestConfig())
-def test_toggle_max(self):
+@manager_config
+@no_xinerama
+def test_toggle_max(qtile):
+    self = qtile
+
     # change to 2 col stack
     self.c.next_layout()
     assert len(self.c.layout.info()["stacks"]) == 2
@@ -502,24 +574,27 @@ def test_toggle_max(self):
     assert self.c.window.info()['y'] == 0
 
     self.c.window.toggle_maximize()
-    assert self.c.window.info()['floating'] == True
-    assert self.c.window.info()['maximized'] == True
+    assert self.c.window.info()['floating'] is True
+    assert self.c.window.info()['maximized'] is True
     assert self.c.window.info()['width'] == 800
     assert self.c.window.info()['height'] == 580
     assert self.c.window.info()['x'] == 0
     assert self.c.window.info()['y'] == 0
 
     self.c.window.toggle_maximize()
-    assert self.c.window.info()['floating'] == False
-    assert self.c.window.info()['maximized'] == False
+    assert self.c.window.info()['floating'] is False
+    assert self.c.window.info()['maximized'] is False
     assert self.c.window.info()['width'] == 398
     assert self.c.window.info()['height'] == 578
     assert self.c.window.info()['x'] == 400
     assert self.c.window.info()['y'] == 0
 
 
-@Xephyr(False, TestConfig())
-def test_toggle_min(self):
+@manager_config
+@no_xinerama
+def test_toggle_min(qtile):
+    self = qtile
+
     # change to 2 col stack
     self.c.next_layout()
     assert len(self.c.layout.info()["stacks"]) == 2
@@ -536,8 +611,8 @@ def test_toggle_min(self):
 
     self.c.window.toggle_minimize()
     assert self.c.group.info()['focus'] == 'xeyes'
-    assert self.c.window.info()['floating'] == True
-    assert self.c.window.info()['minimized'] == True
+    assert self.c.window.info()['floating'] is True
+    assert self.c.window.info()['minimized'] is True
     assert self.c.window.info()['width'] == 398
     assert self.c.window.info()['height'] == 578
     assert self.c.window.info()['x'] == 400
@@ -545,38 +620,44 @@ def test_toggle_min(self):
 
     self.c.window.toggle_minimize()
     assert self.c.group.info()['focus'] == 'xeyes'
-    assert self.c.window.info()['floating'] == False
-    assert self.c.window.info()['minimized'] == False
+    assert self.c.window.info()['floating'] is False
+    assert self.c.window.info()['minimized'] is False
     assert self.c.window.info()['width'] == 398
     assert self.c.window.info()['height'] == 578
     assert self.c.window.info()['x'] == 400
     assert self.c.window.info()['y'] == 0
 
 
-@Xephyr(False, TestConfig())
-def test_toggle_floating(self):
+@manager_config
+@no_xinerama
+def test_toggle_floating(qtile):
+    self = qtile
+
     self.testXeyes()
-    assert self.c.window.info()['floating'] == False
+    assert self.c.window.info()['floating'] is False
     self.c.window.toggle_floating()
-    assert self.c.window.info()['floating'] == True
+    assert self.c.window.info()['floating'] is True
     self.c.window.toggle_floating()
-    assert self.c.window.info()['floating'] == False
+    assert self.c.window.info()['floating'] is False
     self.c.window.toggle_floating()
-    assert self.c.window.info()['floating'] == True
+    assert self.c.window.info()['floating'] is True
 
-    #change layout (should still be floating)
+    # change layout (should still be floating)
     self.c.next_layout()
-    assert self.c.window.info()['floating'] == True
+    assert self.c.window.info()['floating'] is True
+
 
+@manager_config
+@no_xinerama
+def test_floating_focus(qtile):
+    self = qtile
 
-@Xephyr(False, TestConfig())
-def test_floating_focus(self):
     # change to 2 col stack
     self.c.next_layout()
     assert len(self.c.layout.info()["stacks"]) == 2
     self.testXterm()
     self.testXeyes()
-    #self.testWindow("one")
+    # self.testWindow("one")
     assert self.c.window.info()['width'] == 398
     assert self.c.window.info()['height'] == 578
     self.c.window.toggle_floating()
@@ -617,17 +698,20 @@ def test_floating_focus(self):
     assert [x['current'] for x in self.c.layout.info()['stacks']] == [0, 0]
 
 
-@Xephyr(False, TestConfig())
-def test_move_floating(self):
+@manager_config
+@no_xinerama
+def test_move_floating(qtile):
+    self = qtile
+
     self.testXeyes()
-    #self.testWindow("one")
+    # self.testWindow("one")
     assert self.c.window.info()['width'] == 798
     assert self.c.window.info()['height'] == 578
 
     assert self.c.window.info()['x'] == 0
     assert self.c.window.info()['y'] == 0
     self.c.window.toggle_floating()
-    assert self.c.window.info()['floating'] == True
+    assert self.c.window.info()['floating'] is True
 
     self.c.window.move_floating(10, 20, 42, 42)
     assert self.c.window.info()['width'] == 150
@@ -653,7 +737,7 @@ def test_move_floating(self):
     assert self.c.window.info()['x'] == 10
     assert self.c.window.info()['y'] == 20
 
-    #change layout (x, y should be same)
+    # change layout (x, y should be same)
     self.c.next_layout()
     assert self.c.window.info()['width'] == 10
     assert self.c.window.info()['height'] == 20
@@ -661,13 +745,19 @@ def test_move_floating(self):
     assert self.c.window.info()['y'] == 20
 
 
-@Xephyr(False, TestConfig(), randr=True)
-def test_screens(self):
+@manager_config
+@no_xinerama
+def test_screens(qtile):
+    self = qtile
+
     assert len(self.c.screens())
 
 
-@Xephyr(False, TestConfig(), randr=True)
-def test_rotate(self):
+@manager_config
+@no_xinerama
+def test_rotate(qtile):
+    self = qtile
+
     self.testWindow("one")
     s = self.c.screens()[0]
     height, width = s["height"], s["width"]
@@ -691,8 +781,11 @@ def test_rotate(self):
 
 
 # TODO: see note on test_resize
-@Xephyr(False, TestConfig(), randr=True)
-def test_resize_(self):
+@manager_config
+@no_xinerama
+def test_resize_(qtile):
+    self = qtile
+
     self.testWindow("one")
     subprocess.call(
         [
@@ -711,133 +804,138 @@ def test_resize_(self):
         raise AssertionError("Screen did not resize")
 
 
-@Xephyr(False, TestConfig())
-def test_focus_stays_on_layout_switch(xephyr):
-    xephyr.testWindow("one")
-    xephyr.testWindow("two")
+@manager_config
+@no_xinerama
+def test_focus_stays_on_layout_switch(qtile):
+    qtile.testWindow("one")
+    qtile.testWindow("two")
 
     # switch to a double stack layout
-    xephyr.c.next_layout()
+    qtile.c.next_layout()
 
     # focus on a different window than the default
-    xephyr.c.layout.next()
+    qtile.c.layout.next()
 
     # toggle the layout
-    xephyr.c.next_layout()
-    xephyr.c.prev_layout()
-
-    assert xephyr.c.window.info()['name'] == 'one'
-
-# Due to https://github.com/nose-devs/nose/issues/478, nose 1.1.2 ignores
-# attributes on yielded functions. Workaround is to attach the attribute
-# to the generator function. Can be removed once the issue is resolved.
-@attr('xephyr')
-def qtile_tests():
-    for config in (BareConfig, TestConfig):
-        for xinerama in (True, False):
-            @Xephyr(xinerama, config)
-            def test_xeyes(self):
-                self.testXeyes()
-            yield test_xeyes
-
-            @Xephyr(xinerama, config)
-            def test_xterm(self):
-                self.testXterm()
-            yield test_xterm
-
-            @Xephyr(xinerama, config)
-            def test_xterm_kill(self):
-                self.testXterm()
-                self.c.window.kill()
-                self.c.sync()
-                for _ in range(10):
-                    time.sleep(0.1)
-                    if not self.c.windows():
-                        break
-                else:
-                    raise AssertionError("xterm did not die")
-            yield test_xterm_kill
-
-            @Xephyr(xinerama, config)
-            def test_mapRequest(self):
-                self.testWindow("one")
-                info = self.c.groups()["a"]
-                assert "one" in info["windows"]
-                assert info["focus"] == "one"
-
-                self.testWindow("two")
-                info = self.c.groups()["a"]
-                assert "two" in info["windows"]
-                assert info["focus"] == "two"
-            yield test_mapRequest
-
-            @Xephyr(xinerama, config)
-            def test_unmap(self):
-                one = self.testWindow("one")
-                two = self.testWindow("two")
-                three = self.testWindow("three")
-                info = self.c.groups()["a"]
-                assert info["focus"] == "three"
-
-                assert len(self.c.windows()) == 3
-                self.kill(three)
-
-                assert len(self.c.windows()) == 2
-                info = self.c.groups()["a"]
-                assert info["focus"] == "two"
-
-                self.kill(two)
-                assert len(self.c.windows()) == 1
-                info = self.c.groups()["a"]
-                assert info["focus"] == "one"
-
-                self.kill(one)
-                assert len(self.c.windows()) == 0
-                info = self.c.groups()["a"]
-                assert info["focus"] == None
-            yield test_unmap
-
-            @Xephyr(xinerama, config)
-            def test_setgroup(self):
-                self.testWindow("one")
-                self.c.group["b"].toscreen()
-                self.groupconsistency()
-                if len(self.c.screens()) == 1:
-                    assert self.c.groups()["a"]["screen"] == None
-                else:
-                    assert self.c.groups()["a"]["screen"] == 1
-                assert self.c.groups()["b"]["screen"] == 0
-                self.c.group["c"].toscreen()
-                self.groupconsistency()
-                assert self.c.groups()["c"]["screen"] == 0
-            yield test_setgroup
-
-            @Xephyr(xinerama, config)
-            def test_unmap_noscreen(self):
-                self.testWindow("one")
-                pid = self.testWindow("two")
-                assert len(self.c.windows()) == 2
-                self.c.group["c"].toscreen()
-                self.groupconsistency()
-                self.c.status()
-                assert len(self.c.windows()) == 2
-                self.kill(pid)
-                assert len(self.c.windows()) == 1
-                assert self.c.groups()["a"]["focus"] == "one"
-            yield test_unmap_noscreen
+    qtile.c.next_layout()
+    qtile.c.prev_layout()
+
+    assert qtile.c.window.info()['name'] == 'one'
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_xeyes(qtile):
+    qtile.testXeyes()
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_xterm(qtile):
+    qtile.testXterm()
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_xterm_kill_window(qtile):
+    self = qtile
+
+    self.testXterm()
+    self.c.window.kill()
+    self.c.sync()
+    for _ in range(10):
+        time.sleep(0.1)
+        if not self.c.windows():
+            break
+    else:
+        raise AssertionError("xterm did not die")
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_mapRequest(qtile):
+    self = qtile
+
+    self.testWindow("one")
+    info = self.c.groups()["a"]
+    assert "one" in info["windows"]
+    assert info["focus"] == "one"
+
+    self.testWindow("two")
+    info = self.c.groups()["a"]
+    assert "two" in info["windows"]
+    assert info["focus"] == "two"
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_unmap(qtile):
+    self = qtile
+
+    one = self.testWindow("one")
+    two = self.testWindow("two")
+    three = self.testWindow("three")
+    info = self.c.groups()["a"]
+    assert info["focus"] == "three"
+
+    assert len(self.c.windows()) == 3
+    self.kill_window(three)
+
+    assert len(self.c.windows()) == 2
+    info = self.c.groups()["a"]
+    assert info["focus"] == "two"
+
+    self.kill_window(two)
+    assert len(self.c.windows()) == 1
+    info = self.c.groups()["a"]
+    assert info["focus"] == "one"
+
+    self.kill_window(one)
+    assert len(self.c.windows()) == 0
+    info = self.c.groups()["a"]
+    assert info["focus"] is None
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_setgroup(qtile):
+    self = qtile
+
+    self.testWindow("one")
+    self.c.group["b"].toscreen()
+    self.groupconsistency()
+    if len(self.c.screens()) == 1:
+        assert self.c.groups()["a"]["screen"] is None
+    else:
+        assert self.c.groups()["a"]["screen"] == 1
+    assert self.c.groups()["b"]["screen"] == 0
+    self.c.group["c"].toscreen()
+    self.groupconsistency()
+    assert self.c.groups()["c"]["screen"] == 0
+
+
+@pytest.mark.parametrize("qtile", [BareConfig, ManagerConfig], indirect=True)
+@pytest.mark.parametrize("xephyr", [{"xinerama": True}, {"xinerama": False}], indirect=True)
+def test_unmap_noscreen(qtile):
+    self = qtile
+
+    self.testWindow("one")
+    pid = self.testWindow("two")
+    assert len(self.c.windows()) == 2
+    self.c.group["c"].toscreen()
+    self.groupconsistency()
+    self.c.status()
+    assert len(self.c.windows()) == 2
+    self.kill_window(pid)
+    assert len(self.c.windows()) == 1
+    assert self.c.groups()["a"]["focus"] == "one"
 
 
 def test_init():
-    assert_raises(
-        libqtile.manager.QtileError,
-        libqtile.config.Key,
-        [], "unknown", libqtile.command._Call("base", None, "foo")
-    )
-    assert_raises(
-        libqtile.manager.QtileError,
-        libqtile.config.Key,
-        ["unknown"], "x", libqtile.command._Call("base", None, "foo")
-    )
+    with pytest.raises(libqtile.manager.QtileError):
+        libqtile.config.Key([], "unknown", libqtile.command._Call("base", None, "foo"))
+    with pytest.raises(libqtile.manager.QtileError):
+        libqtile.config.Key(["unknown"], "x", libqtile.command._Call("base", None, "foo"))
 
 
 class TScreen(libqtile.config.Screen):
@@ -873,7 +971,7 @@ def test_dheight():
     assert s.dheight == 80
 
 
-class _Config:
+class _Config(object):
     groups = [
         libqtile.config.Group("a"),
         libqtile.config.Group("b"),
@@ -881,9 +979,9 @@ class _Config:
         libqtile.config.Group("d")
     ]
     layouts = [
-                libqtile.layout.stack.Stack(num_stacks=1),
-                libqtile.layout.stack.Stack(num_stacks=2)
-            ]
+        libqtile.layout.stack.Stack(num_stacks=1),
+        libqtile.layout.stack.Stack(num_stacks=2)
+    ]
     floating_layout = libqtile.layout.floating.Floating()
     keys = [
         libqtile.config.Key(
@@ -899,12 +997,12 @@ class _Config:
     ]
     mouse = []
     screens = [libqtile.config.Screen(
-            bottom=libqtile.bar.Bar(
-                        [
-                            libqtile.widget.GroupBox(),
-                        ],
-                        20
-                    ),
+        bottom=libqtile.bar.Bar(
+            [
+                libqtile.widget.GroupBox(),
+            ],
+            20
+        ),
     )]
     auto_fullscreen = True
 
@@ -917,16 +1015,22 @@ def client_new(c):
         libqtile.hook.subscribe.client_new(client_new)
 
 
-@Xephyr(False, ClientNewStaticConfig())
-def test_minimal_(self):
+clientnew_config = pytest.mark.parametrize("qtile", [ClientNewStaticConfig], indirect=True)
+
+
+@clientnew_config
+def test_minimal_(qtile):
+    self = qtile
+
     a = self.testWindow("one")
-    self.kill(a)
+    self.kill_window(a)
 
-if utils.whereis("gkrellm"):
-    @Xephyr(False, ClientNewStaticConfig())
-    def test_gkrellm(self):
-        self.testGkrellm()
-        time.sleep(0.1)
+
+@pytest.mark.skipif(whereis("gkrellm") is None, reason="gkrellm not found")
+@clientnew_config
+def test_gkrellm(qtile):
+    qtile.testGkrellm()
+    time.sleep(0.1)
 
 
 class ToGroupConfig(_Config):
@@ -937,15 +1041,19 @@ def client_new(c):
         libqtile.hook.subscribe.client_new(client_new)
 
 
-@Xephyr(False, ToGroupConfig())
-def test_minimal__(self):
-    self.c.group["d"].toscreen()
-    self.c.group["a"].toscreen()
-    a = self.testWindow("one")
-    assert len(self.c.group["d"].info()["windows"]) == 1
-    self.kill(a)
+togroup_config = pytest.mark.parametrize("qtile", [ToGroupConfig], indirect=True)
+
+
+@togroup_config
+def test_minimal__(qtile):
+    qtile.c.group["d"].toscreen()
+    qtile.c.group["a"].toscreen()
+    a = qtile.testWindow("one")
+    assert len(qtile.c.group["d"].info()["windows"]) == 1
+    qtile.kill_window(a)
+
 
-@Xephyr(False, TestConfig)
-def test_colorPixel(self):
+@manager_config
+def test_colorPixel(qtile):
     # test for #394
-    self.c.eval("self.colorPixel(\"ffffff\")")
+    qtile.c.eval("self.colorPixel(\"ffffff\")")
diff --git a/test/test_sh.py b/test/test_sh.py
--- a/test/test_sh.py
+++ b/test/test_sh.py
@@ -20,13 +20,14 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import pytest
+
 import libqtile
 import libqtile.sh
 import libqtile.confreader
 import libqtile.layout
 import libqtile.manager
 import libqtile.config
-from .utils import Xephyr
 
 
 class ShConfig(object):
@@ -46,91 +47,93 @@ class ShConfig(object):
     main = None
 
 
-@Xephyr(True, ShConfig())
-def test_columnize(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    assert self.sh.columnize(["one", "two"]) == "one  two"
+sh_config = pytest.mark.parametrize("qtile", [ShConfig], indirect=True)
+
+
+@sh_config
+def test_columnize(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    assert qtile.sh.columnize(["one", "two"]) == "one  two"
 
-    self.sh.termwidth = 1
-    assert self.sh.columnize(["one", "two"], update_termwidth=False) == "one\ntwo"
+    qtile.sh.termwidth = 1
+    assert qtile.sh.columnize(["one", "two"], update_termwidth=False) == "one\ntwo"
 
-    self.sh.termwidth = 15
-    v = self.sh.columnize(["one", "two", "three", "four", "five"], update_termwidth=False)
+    qtile.sh.termwidth = 15
+    v = qtile.sh.columnize(["one", "two", "three", "four", "five"], update_termwidth=False)
     assert v == 'one    two  \nthree  four \nfive '
 
 
-@Xephyr(True, ShConfig())
-def test_ls(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    self.sh.do_cd("layout")
-    self.sh.do_ls("")
+@sh_config
+def test_ls(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    qtile.sh.do_cd("layout")
+    qtile.sh.do_ls("")
 
 
-@Xephyr(True, ShConfig())
-def test_findNode(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    n = self.sh._findNode(self.sh.current, "layout")
+@sh_config
+def test_findNode(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    n = qtile.sh._findNode(qtile.sh.current, "layout")
     assert n.path == "layout"
     assert n.parent
 
-    n = self.sh._findNode(n, "0")
+    n = qtile.sh._findNode(n, "0")
     assert n.path == "layout[0]"
 
-    n = self.sh._findNode(n, "..")
+    n = qtile.sh._findNode(n, "..")
     assert n.path == "layout"
 
-    n = self.sh._findNode(n, "0", "..")
+    n = qtile.sh._findNode(n, "0", "..")
     assert n.path == "layout"
 
-    n = self.sh._findNode(n, "..", "layout", 0)
+    n = qtile.sh._findNode(n, "..", "layout", 0)
     assert n.path == "layout[0]"
 
-    assert not self.sh._findNode(n, "wibble")
-    assert not self.sh._findNode(n, "..", "0", "wibble")
+    assert not qtile.sh._findNode(n, "wibble")
+    assert not qtile.sh._findNode(n, "..", "0", "wibble")
 
 
-@Xephyr(True, ShConfig())
-def test_do_cd(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    assert self.sh.do_cd("layout") == 'layout'
-    assert self.sh.do_cd("0/wibble") == 'No such path.'
-    assert self.sh.do_cd("0/") == 'layout[0]'
+@sh_config
+def test_do_cd(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    assert qtile.sh.do_cd("layout") == 'layout'
+    assert qtile.sh.do_cd("0/wibble") == 'No such path.'
+    assert qtile.sh.do_cd("0/") == 'layout[0]'
 
 
-@Xephyr(True, ShConfig())
-def test_call(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    assert self.sh._call("status", []) == "OK"
+@sh_config
+def test_call(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    assert qtile.sh._call("status", []) == "OK"
 
-    v = self.sh._call("nonexistent", "")
+    v = qtile.sh._call("nonexistent", "")
     assert "No such command" in v
 
-    v = self.sh._call("status", "(((")
+    v = qtile.sh._call("status", "(((")
     assert "Syntax error" in v
 
-    v = self.sh._call("status", "(1)")
+    v = qtile.sh._call("status", "(1)")
     assert "Command exception" in v
 
 
-@Xephyr(True, ShConfig())
-def test_complete(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    assert self.sh._complete("c", "c") == [
+@sh_config
+def test_complete(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    assert qtile.sh._complete("c", "c") == [
         "cd",
         "commands",
         "critical",
     ]
 
-    assert self.sh._complete("cd l", "l") == ["layout"]
-    print(self.sh._complete("cd layout/", "layout/"))
-    assert self.sh._complete("cd layout/", "layout/") == [
+    assert qtile.sh._complete("cd l", "l") == ["layout"]
+    assert qtile.sh._complete("cd layout/", "layout/") == [
         "layout/" + x for x in ["group", "window", "screen", "0"]
     ]
-    assert self.sh._complete("cd layout/", "layout/g") == ["layout/group"]
+    assert qtile.sh._complete("cd layout/", "layout/g") == ["layout/group"]
 
 
-@Xephyr(True, ShConfig())
-def test_help(self):
-    self.sh = libqtile.sh.QSh(self.c)
-    assert self.sh.do_help("nonexistent").startswith("No such command")
-    assert self.sh.do_help("help")
+@sh_config
+def test_help(qtile):
+    qtile.sh = libqtile.sh.QSh(qtile.c)
+    assert qtile.sh.do_help("nonexistent").startswith("No such command")
+    assert qtile.sh.do_help("help")
diff --git a/test/test_utils.py b/test/test_utils.py
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -101,9 +101,11 @@ def test_rgb_from_base10_tuple():
 def test_rgb_from_base10_tuple_with_alpha():
     assert utils.rgb([255, 255, 0, 0.5]) == (1, 1, 0, 0.5)
 
+
 def test_scrub_to_utf8():
     assert utils.scrub_to_utf8(six.b("foo")) == six.u("foo")
 
+
 def test_shuffle():
     l = list(range(3))
     utils.shuffleUp(l)
diff --git a/test/test_widget.py b/test/test_widget.py
--- a/test/test_widget.py
+++ b/test/test_widget.py
@@ -21,17 +21,16 @@
 
 # Widget specific tests
 
+import pytest
+
 from libqtile.config import Screen
 from libqtile.bar import Bar
 from libqtile.widget import TextBox, ThermalSensor
 
-
-from .utils import Xephyr
-from .test_manager import BareConfig
+from .conftest import BareConfig
 
 
 class ColorChanger(TextBox):
-
     count = 0
 
     def update(self, text):
@@ -47,14 +46,16 @@ class WidgetTestConf(BareConfig):
     screens = [Screen(bottom=Bar([ColorChanger(name="colorchanger")], 20))]
 
 
-@Xephyr(False, WidgetTestConf())
-def test_textbox_color_change(self):
-    self.c.widget["colorchanger"].update('f')
-    print(self.c.widget["colorchanger"].info())
-    assert self.c.widget["colorchanger"].info()["foreground"] == "0000ff"
+widget_conf = pytest.mark.parametrize("qtile", [WidgetTestConf], indirect=True)
+
+
+@widget_conf
+def test_textbox_color_change(qtile):
+    qtile.c.widget["colorchanger"].update('f')
+    assert qtile.c.widget["colorchanger"].info()["foreground"] == "0000ff"
 
-    self.c.widget["colorchanger"].update('f')
-    assert self.c.widget["colorchanger"].info()["foreground"] == "ff0000"
+    qtile.c.widget["colorchanger"].update('f')
+    assert qtile.c.widget["colorchanger"].info()["foreground"] == "ff0000"
 
 
 def test_thermalsensor_regex_compatibility():
diff --git a/test/utils.py b/test/utils.py
deleted file mode 100644
--- a/test/utils.py
+++ /dev/null
@@ -1,393 +0,0 @@
-# Copyright (c) 2011 Florian Mounier
-# Copyright (c) 2011 Anshuman Bhaduri
-# Copyright (c) 2014 Sean Vig
-# Copyright (c) 2014-2015 Tycho Andersen
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-import libqtile
-import libqtile.ipc
-from libqtile.manager import Qtile
-from libqtile.log_utils import init_log
-
-import logging
-import multiprocessing
-import os
-import shutil
-import subprocess
-import sys
-import tempfile
-import time
-import traceback
-import xcffib
-import xcffib.xproto
-from nose.tools import with_setup, assert_raises
-from nose.plugins.attrib import attr
-from functools import wraps
-WIDTH = 800
-HEIGHT = 600
-SECOND_WIDTH = 640
-SECOND_HEIGHT = 480
-
-
-def _find_display():
-    """
-        Returns the next available display
-    """
-    display = 1
-    while os.path.exists("/tmp/.X%s-lock" % display):
-        display += 1
-    return display
-
-
-def whereis(program):
-    for path in os.environ.get('PATH', '').split(':'):
-        if os.path.exists(os.path.join(path, program)) and \
-           not os.path.isdir(os.path.join(path, program)):
-            return os.path.join(path, program)
-    return None
-
-
-class BareConfig:
-    auto_fullscreen = True
-    groups = [
-        libqtile.config.Group("a"),
-        libqtile.config.Group("b"),
-        libqtile.config.Group("c"),
-        libqtile.config.Group("d")
-    ]
-    layouts = [
-                libqtile.layout.stack.Stack(num_stacks=1),
-                libqtile.layout.stack.Stack(num_stacks=2)
-            ]
-    floating_layout = libqtile.layout.floating.Floating()
-    keys = [
-        libqtile.config.Key(
-            ["control"],
-            "k",
-            libqtile.command._Call([("layout", None)], "up")
-        ),
-        libqtile.config.Key(
-            ["control"],
-            "j",
-            libqtile.command._Call([("layout", None)], "down")
-        ),
-    ]
-    mouse = []
-    screens = [libqtile.config.Screen()]
-    main = None
-    follow_mouse_focus = False
-
-class Xephyr(object):
-    def __init__(self, xinerama, config, start_qtile=True,
-                 randr=False, two_screens=True,
-                 width=WIDTH, height=HEIGHT, xoffset=None):
-        self.xinerama, self.randr = xinerama, randr
-        self.config = config
-        self.start_qtile = start_qtile
-        self.two_screens = two_screens
-
-
-        self.width = width
-        self.height = height
-        if xoffset is None:
-            self.xoffset = width
-        else:
-            self.xoffset = xoffset
-
-        self.qtile = None # Handle to Qtile instance, multiprocessing.Process object
-        self.xephyr = None # Handle to Xephyr instance, subprocess.Popen object
-        self.display = ":{}".format(_find_display())
-
-    def __call__(self, function):
-        def teardown():
-            # Remove temporary files
-            if os.path.exists(self.tempdir):
-                shutil.rmtree(self.tempdir)
-
-            # Shutdown Xephyr
-            self._stopXephyr()
-
-        def setup():
-            # Setup socket and log files
-            self.tempdir = tempfile.mkdtemp()
-            self.sockfile = os.path.join(self.tempdir, 'qtile.sock')
-            self.logfile = os.path.join(self.tempdir, 'qtile.log')
-
-            self.testwindows = []
-
-            # Setup Xephyr
-            try:
-                self._startXephyr()
-            except AssertionError:
-                teardown()
-                raise
-
-        @attr('xephyr')
-        @with_setup(setup, teardown)
-        @wraps(function)
-        def wrapped_fun():
-            try:
-                if self.start_qtile:
-                    self.startQtile(self.config)
-                return function(self)
-            finally:
-                if os.path.exists(self.logfile):
-                    with open(self.logfile) as f:
-                        log_output = f.read().strip()
-                    if log_output:
-                        print("------------------------ >> begin log file << ------------------------")
-                        print(log_output)
-                        print("------------------------- >> end log file << -------------------------")
-                    else:
-                        print("------------------------ >> log file empty << ------------------------")
-                # If we started qtile, we should be sure to take it down
-                if self.start_qtile:
-                    self.stopQtile()
-                # If we didn't start qtile, make sure the user took it down if needed
-                if self.qtile and not self.start_qtile:
-                    raise AssertionError("Stop qtile!")
-
-        return wrapped_fun
-
-    def _startXephyr(self, restart=True):
-        args = [
-            "Xephyr", "-name", "qtile_test",
-            self.display, "-ac",
-            "-screen", "%sx%s" % (self.width, self.height)]
-        if self.two_screens:
-            args.extend(["-origin", "%s,0" % self.xoffset, "-screen",
-                "%sx%s" % (SECOND_WIDTH, SECOND_HEIGHT)])
-
-        if self.xinerama:
-            args.extend(["+xinerama"])
-        if self.randr:
-            args.extend(["+extension", "RANDR"])
-
-        self.xephyr = subprocess.Popen(args,
-            stdout=subprocess.PIPE, stderr=subprocess.PIPE
-        )
-
-        try:
-            self._waitForXephyr()
-        except AssertionError:
-            # We'll try to get a new display and try one more time...
-            if restart:
-                self.display = ":{}".format(_find_display())
-                self._startXephyr(restart=False)
-            else:
-                raise
-
-    def _stopXephyr(self):
-        assert self.xephyr is not None, "Xephyr must be started first"
-
-        # Complain if qtile or windows are running, but still kill xephyr
-        try:
-            assert self.qtile is None, "Kill Qtile before stopping Xephyr"
-            assert self.testwindows == [], "Kill all test windows before stopping Xephyr"
-        finally:
-            # Kill xephyr only if it is running
-            if self.xephyr.poll() is None:
-                self._kill(self.xephyr)
-            self.xephyr = None
-
-    def _waitForXephyr(self):
-        # Wait until Xephyr process dies
-        while self.xephyr.poll() is None:
-            try:
-                conn = xcffib.connect(self.display)
-                break
-            except xcffib.ConnectionException:
-                pass
-            time.sleep(0.1)
-        else:
-            (stdout_data, stderr_data) = self.xephyr.communicate()
-            raise AssertionError("Error launching Xephyr, quit with return code: {:d}\n"
-                                 "stderr: {}\n"
-                                 "stdout: {}".format(
-                                     self.xephyr.returncode,
-                                     stderr_data.decode(),
-                                     stdout_data.decode()
-                                 )
-            )
-
-        conn.disconnect()
-        del conn
-
-    def startQtile(self, config):
-        rpipe, wpipe = multiprocessing.Pipe()
-
-        def runQtile():
-            try:
-                init_log(logging.INFO, log_path=self.logfile, log_color=False)
-                q = Qtile(config, self.display, self.sockfile)
-                q.loop()
-            except Exception:
-                wpipe.send(traceback.format_exc())
-                with open(self.logfile, 'a') as f:
-                    f.write("--------------------- >> begin qtile traceback << --------------------")
-                    f.write(traceback.format_exc())
-                    f.write("-------------------- >> begin qtile traceback << ---------------------")
-
-        self.qtile = multiprocessing.Process(target=runQtile)
-        self.qtile.start()
-
-        self._waitForQtile(rpipe)
-
-    def stopQtile(self):
-        assert self.qtile is not None, "Qtile must be started first"
-
-        self.qtile.terminate()
-        self.qtile.join(10)
-
-        if self.qtile.is_alive():
-            # desparate times... this probably messes with multiprocessing...
-            try:
-                os.kill(self.qtile.pid, 9)
-                os.waitpid(self.qtile.pid, 0)
-            except OSError:
-                # The process may have died due to some other error
-                pass
-
-        if self.qtile.exitcode:
-            print("Qtile exited with exitcode: %d" % self.qtile.exitcode)
-
-        self.qtile = None
-
-        # Kill all the windows
-        for proc in self.testwindows[:]:
-            self._kill(proc)
-
-    def _waitForQtile(self, errpipe):
-        # First, wait for socket to appear
-        start = time.time()
-        while time.time() < start + 10:
-            if os.path.exists(self.sockfile):
-                break
-
-            if errpipe.poll(0.1):
-                error = errpipe.recv()
-                raise AssertionError("Error launching Qtile, traceback:\n%s" % error)
-        else:
-            raise AssertionError("Error launching Qtile, socket never came up")
-
-        self.c = libqtile.command.Client(self.sockfile)
-
-        # Next, wait for server to come up
-        start = time.time()
-        while time.time() < start + 10:
-            try:
-                if self.c.status() == "OK":
-                    break
-            except libqtile.ipc.IPCError:
-                pass
-
-            if errpipe.poll(0.1):
-                error = errpipe.recv()
-                raise AssertionError("Error launching Qtile, traceback:\n%s" % error)
-        else:
-            raise AssertionError("Error launching Qtile, quit without exception")
-
-    def _testProc(self, args):
-        if not args:
-            raise AssertionError("Trying to run nothing! (missing arguments)")
-        start = len(self.c.windows())
-
-        proc = subprocess.Popen(args, env={"DISPLAY": self.display})
-
-        while proc.poll() is None:
-            try:
-                if len(self.c.windows()) > start:
-                    break
-            except RuntimeError:
-                pass
-            time.sleep(0.1)
-        else:
-            raise AssertionError("Window never appeared...")
-
-        self.testwindows.append(proc)
-        return proc
-
-    def groupconsistency(self):
-        groups = self.c.groups()
-        screens = self.c.screens()
-        seen = set()
-        for g in groups.values():
-            scrn = g["screen"]
-            if scrn is not None:
-                if scrn in seen:
-                    raise AssertionError(
-                        "Screen referenced from more than one group.")
-                seen.add(scrn)
-                assert screens[scrn]["group"] == g["name"]
-        assert len(seen) == len(screens), "Not all screens \
-        had an attached group."
-
-    def qtileRaises(self, exc, config):
-        assert_raises(exc, Qtile,
-                      config, self.display, self.sockfile)
-
-    def testWindow(self, name):
-        python = sys.executable
-        d = os.path.dirname(os.path.realpath(__file__))
-        python = sys.executable
-        path = os.path.join(d, "scripts", "window.py")
-        return self._testProc(
-                    [python, path, self.display, name]
-                )
-
-    def testXclock(self):
-        path = whereis("xclock")
-        return self._testProc(
-                    [path]
-                )
-
-    def testXeyes(self):
-        path = whereis("xeyes")
-        return self._testProc(
-                    [path]
-                )
-
-    def testGkrellm(self):
-        path = whereis("gkrellm")
-        return self._testProc(
-                    [path]
-                )
-
-    def testXterm(self):
-        path = whereis("xterm")
-        return self._testProc(
-                    [path]
-                )
-
-    def _kill(self, proc):
-        proc.kill()
-        proc.wait()
-        if proc in self.testwindows:
-            self.testwindows.remove(proc)
-
-    def kill(self, proc):
-        start = len(self.c.windows())
-        self._kill(proc)
-        for i in range(20):
-            if len(self.c.windows()) < start:
-                break
-            time.sleep(0.1)
-        else:
-            raise AssertionError("Window could not be killed...")