If you have used Xvfb to run Firefox or Selenium in a headless environment, the basic setup may look familiar. The tricky part is usually not starting Xvfb, but knowing which virtual screen your application is actually using, and then being able to inspect or record that screen when something goes wrong.

Which Xvfb screen is actually being used?

A common first attempt is:

Xvfb :1 -screen 1 1600x1200x16 &

That command defines screen 1 with a 1600x1200x16 configuration. But Xvfb also creates a screen 0 automatically, with a default configuration of 1280x1024x8. If you then start Firefox with:

DISPLAY=:1 firefox http://www.qq.com

the application will use the default screen 0, not the 1600x1200 screen that you explicitly defined. This is easy to miss because the display number is correct; only the screen number is wrong.

There are two ways to avoid the problem. The first is to override the default screen definition:

Xvfb :1 -screen 0 1600x1200x16 &

The second is to keep the screen 1 definition and point the application at that screen:

DISPLAY=:1.1 firefox http://www.qq.com

In the :1.1 notation, the first number is the Xvfb server number, and the second is the screen number. So :1.1 means server 1, screen 1.

Taking screenshots of an Xvfb session

A screenshot is useful when a scheduled Selenium job stops producing log output, exits without an exception, or appears stuck. You can capture the virtual display and see what is actually on screen.

Install ImageMagick, which provides the import command. Either of these forms works:

DISPLAY=:1.1 import -window root screenshot.png
import -window root -display :1.1 -screen screenshot.png

As with the earlier examples, pay attention to the server and screen numbers. The same rule applies when you use xvfb-run. Its default server number is 99, so the display is usually :99 unless you change it. If Selenium is not running an application on that display, the screenshot will be entirely black.

Recording the virtual display

Sometimes a screenshot is not enough. If local debugging does not match the server, logs do not reveal the problem, and screenshots look strange, you may want to watch the Selenium driver operate on the server. Recording the Xvfb display can help.

Install ffmpeg and run:

ffmpeg -video_size `DISPLAY=:2 xdpyinfo | grep 'dimensions:'|awk '{print $2}'` -framerate 30 -f x11grab -i :2.0 output.mpg

The -i :2.0 argument uses the same server and screen notation as before: server 2, screen 0. The capture area can also be offset or cropped, which is useful if you only need part of the display.

There is also a Node wrapper called x-recorder. The original repository is lightsofapollo/x-recorder, but it has not been updated for a long time. At one point it stopped working with Node.js v5.0, and a pull request that fixed it was not merged. If you need a newer version, look at SteveNewson/x-recorder.

Checking whether Selenium is active by inspecting a screenshot

Because an inactive display produces a black screenshot, you can use the screenshot itself to determine whether the background process is running an application or simply waiting. This is an external check and does not require modifying the program.

When a browser is open and showing a page, the top-left area is part of the browser title bar, so it will not be black. When the task has finished, the display is black.

You can inspect a pixel with Python and Pillow:

from PIL import Image
im = Image.open("screenshot.png")
pix = im.load()
print im.size
print pix[6,6]

For a black screenshot, the pixel value prints as an integer 0. When the browser is open, the same pixel at [6,6] produces a tuple such as (232, 232, 231).