1import ./make-test-python.nix ({ pkgs, firefoxPackage, ... }:
2let firefoxPackage' = firefoxPackage.override (args: {
3 extraPrefsFiles = (args.extraPrefsFiles or []) ++ [
4 # make sure that autoplay is enabled by default for the audio test
5 (builtins.toString (builtins.toFile "autoplay-pref.js" ''defaultPref("media.autoplay.default",0);''))
6 ];
7 });
8
9in
10{
11 name = firefoxPackage'.unwrapped.pname;
12 meta = with pkgs.lib.maintainers; {
13 maintainers = [ eelco shlevy ];
14 };
15
16 nodes.machine =
17 { pkgs, ... }:
18
19 { imports = [ ./common/x11.nix ];
20 environment.systemPackages = [
21 firefoxPackage'
22 pkgs.xdotool
23 ];
24
25 # Create a virtual sound device, with mixing
26 # and all, for recording audio.
27 boot.kernelModules = [ "snd-aloop" ];
28 sound.enable = true;
29 sound.extraConfig = ''
30 pcm.!default {
31 type plug
32 slave.pcm pcm.dmixer
33 }
34 pcm.dmixer {
35 type dmix
36 ipc_key 1
37 slave {
38 pcm "hw:Loopback,0,0"
39 rate 48000
40 periods 128
41 period_time 0
42 period_size 1024
43 buffer_size 8192
44 }
45 }
46 pcm.recorder {
47 type hw
48 card "Loopback"
49 device 1
50 subdevice 0
51 }
52 '';
53
54 systemd.services.audio-recorder = {
55 description = "Record NixOS test audio to /tmp/record.wav";
56 script = "${pkgs.alsa-utils}/bin/arecord -D recorder -f S16_LE -r48000 /tmp/record.wav";
57 };
58
59 };
60
61 testScript = ''
62 from contextlib import contextmanager
63
64
65 @contextmanager
66 def record_audio(machine: Machine):
67 """
68 Perform actions while recording the
69 machine audio output.
70 """
71 machine.systemctl("start audio-recorder")
72 yield
73 machine.systemctl("stop audio-recorder")
74
75
76 def wait_for_sound(machine: Machine):
77 """
78 Wait until any sound has been emitted.
79 """
80 machine.wait_for_file("/tmp/record.wav")
81 while True:
82 # Get at most 2M of the recording
83 machine.execute("tail -c 2M /tmp/record.wav > /tmp/last")
84 # Get the exact size
85 size = int(machine.succeed("stat -c '%s' /tmp/last").strip())
86 # Compare it against /dev/zero using `cmp` (skipping 50B of WAVE header).
87 # If some non-NULL bytes are found it returns 1.
88 status, output = machine.execute(
89 f"cmp -i 50 -n {size - 50} /tmp/last /dev/zero 2>&1"
90 )
91 if status == 1:
92 break
93 machine.sleep(2)
94
95
96 machine.wait_for_x()
97
98 with subtest("Wait until Firefox has finished loading the Valgrind docs page"):
99 machine.execute(
100 "xterm -e '${firefoxPackage'.unwrapped.binaryName} file://${pkgs.valgrind.doc}/share/doc/valgrind/html/index.html' >&2 &"
101 )
102 machine.wait_for_window("Valgrind")
103 machine.sleep(40)
104
105 with subtest("Check whether Firefox can play sound"):
106 with record_audio(machine):
107 machine.succeed(
108 "${firefoxPackage'.unwrapped.binaryName} file://${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/phone-incoming-call.oga >&2 &"
109 )
110 wait_for_sound(machine)
111 machine.copy_from_vm("/tmp/record.wav")
112
113 with subtest("Close sound test tab"):
114 machine.execute("xdotool key ctrl+w")
115
116 with subtest("Close default browser prompt"):
117 machine.execute("xdotool key space")
118
119 with subtest("Wait until Firefox draws the developer tool panel"):
120 machine.sleep(10)
121 machine.succeed("xwininfo -root -tree | grep Valgrind")
122 machine.screenshot("screen")
123 '';
124
125})