1{ system ? builtins.currentSystem
2, config ? {}
3, pkgs ? import ../.. { inherit system config; }
4, channelMap ? { # Maps "channels" to packages
5 stable = pkgs.chromium;
6 beta = pkgs.chromiumBeta;
7 dev = pkgs.chromiumDev;
8 ungoogled = pkgs.ungoogled-chromium;
9 chrome-stable = pkgs.google-chrome;
10 chrome-beta = pkgs.google-chrome-beta;
11 chrome-dev = pkgs.google-chrome-dev;
12 }
13}:
14
15with import ../lib/testing-python.nix { inherit system pkgs; };
16with pkgs.lib;
17
18let
19 user = "alice";
20
21 startupHTML = pkgs.writeText "chromium-startup.html" ''
22 <!DOCTYPE html>
23 <html>
24 <head>
25 <meta charset="UTF-8">
26 <title>Chromium startup notifier</title>
27 </head>
28 <body onload="javascript:document.title='startup done'">
29 <img src="file://${pkgs.fetchurl {
30 url = "https://nixos.org/logo/nixos-hex.svg";
31 sha256 = "07ymq6nw8kc22m7kzxjxldhiq8gzmc7f45kq2bvhbdm0w5s112s4";
32 }}" />
33 </body>
34 </html>
35 '';
36in
37
38mapAttrs (channel: chromiumPkg: makeTest {
39 name = "chromium-${channel}";
40 meta = {
41 maintainers = with maintainers; [ aszlig primeos ];
42 } // optionalAttrs (chromiumPkg.meta ? timeout) {
43 # https://github.com/NixOS/hydra/issues/591#issuecomment-435125621
44 # Note: optionalAttrs is used since meta.timeout is not set for Google Chrome
45 inherit (chromiumPkg.meta) timeout;
46 };
47
48 enableOCR = true;
49
50 nodes.machine = { ... }: {
51 imports = [ ./common/user-account.nix ./common/x11.nix ];
52 virtualisation.memorySize = 2047;
53 test-support.displayManager.auto.user = user;
54 environment = {
55 systemPackages = [ chromiumPkg ];
56 variables."XAUTHORITY" = "/home/alice/.Xauthority";
57 };
58 };
59
60 testScript = let
61 xdo = name: text: let
62 xdoScript = pkgs.writeText "${name}.xdo" text;
63 in "${pkgs.xdotool}/bin/xdotool ${xdoScript}";
64 in ''
65 import shlex
66 import re
67 from contextlib import contextmanager
68
69
70 major_version = "${versions.major (getVersion chromiumPkg.name)}"
71
72
73 # Run as user alice
74 def ru(cmd):
75 return "su - ${user} -c " + shlex.quote(cmd)
76
77
78 def launch_browser():
79 """Launches the web browser with the correct options."""
80 # Determine the name of the binary:
81 pname = "${getName chromiumPkg.name}"
82 if pname.find("chromium") != -1:
83 binary = "chromium" # Same name for all channels and ungoogled-chromium
84 elif pname == "google-chrome":
85 binary = "google-chrome-stable"
86 elif pname == "google-chrome-dev":
87 binary = "google-chrome-unstable"
88 else: # For google-chrome-beta and as fallback:
89 binary = pname
90 # Add optional CLI options:
91 options = []
92 if major_version > "95" and not pname.startswith("google-chrome"):
93 # Workaround to avoid a GPU crash:
94 options.append("--use-gl=swiftshader")
95 # Launch the process:
96 options.append("file://${startupHTML}")
97 machine.succeed(ru(f'ulimit -c unlimited; {binary} {shlex.join(options)} >&2 & disown'))
98 if binary.startswith("google-chrome"):
99 # Need to click away the first window:
100 machine.wait_for_text("Make Google Chrome the default browser")
101 machine.screenshot("google_chrome_default_browser_prompt")
102 machine.send_key("ret")
103
104
105 def create_new_win():
106 """Creates a new Chromium window."""
107 with machine.nested("Creating a new Chromium window"):
108 machine.wait_until_succeeds(
109 ru(
110 "${xdo "create_new_win-select_main_window" ''
111 search --onlyvisible --name "startup done"
112 windowfocus --sync
113 windowactivate --sync
114 ''}"
115 )
116 )
117 machine.send_key("ctrl-n")
118 # Wait until the new window appears:
119 machine.wait_until_succeeds(
120 ru(
121 "${xdo "create_new_win-wait_for_window" ''
122 search --onlyvisible --name "New Tab"
123 windowfocus --sync
124 windowactivate --sync
125 ''}"
126 )
127 )
128
129
130 def close_new_tab_win():
131 """Closes the Chromium window with the title "New Tab"."""
132 machine.wait_until_succeeds(
133 ru(
134 "${xdo "close_new_tab_win-select_main_window" ''
135 search --onlyvisible --name "New Tab"
136 windowfocus --sync
137 windowactivate --sync
138 ''}"
139 )
140 )
141 machine.send_key("ctrl-w")
142 # Wait until the closed window disappears:
143 machine.wait_until_fails(
144 ru(
145 "${xdo "close_new_tab_win-wait_for_close" ''
146 search --onlyvisible --name "New Tab"
147 ''}"
148 )
149 )
150
151
152 @contextmanager
153 def test_new_win(description, url, window_name):
154 create_new_win()
155 machine.wait_for_window("New Tab")
156 machine.send_chars(f"{url}\n")
157 machine.wait_for_window(window_name)
158 machine.screenshot(description)
159 machine.succeed(
160 ru(
161 "${xdo "copy-all" ''
162 key --delay 1000 Ctrl+a Ctrl+c
163 ''}"
164 )
165 )
166 clipboard = machine.succeed(
167 ru("${pkgs.xclip}/bin/xclip -o")
168 )
169 if url == "chrome://gpu":
170 clipboard = "" # TODO: We cannot copy the text via Ctrl+a
171 print(f"{description} window content:\n{clipboard}")
172 with machine.nested(description):
173 yield clipboard
174 # Close the newly created window:
175 machine.send_key("ctrl-w")
176
177
178 machine.wait_for_x()
179
180 launch_browser()
181
182 machine.wait_for_text("startup done")
183 machine.wait_until_succeeds(
184 ru(
185 "${xdo "check-startup" ''
186 search --sync --onlyvisible --name "startup done"
187 # close first start help popup
188 key -delay 1000 Escape
189 windowfocus --sync
190 windowactivate --sync
191 ''}"
192 )
193 )
194
195 create_new_win()
196 # Optional: Wait for the new tab page to fully load before taking the screenshot:
197 machine.wait_for_text("Web Store")
198 machine.screenshot("empty_windows")
199 close_new_tab_win()
200
201 machine.screenshot("startup_done")
202
203 with test_new_win("sandbox_info", "chrome://sandbox", "Sandbox Status") as clipboard:
204 filters = [
205 "layer 1 sandbox.*namespace",
206 "pid namespaces.*yes",
207 "network namespaces.*yes",
208 "seccomp.*sandbox.*yes",
209 "you are adequately sandboxed",
210 ]
211 if not all(
212 re.search(filter, clipboard, flags=re.DOTALL | re.IGNORECASE)
213 for filter in filters
214 ):
215 assert False, f"sandbox not working properly: {clipboard}"
216
217 machine.sleep(1)
218 machine.succeed(
219 ru(
220 "${xdo "find-window-after-copy" ''
221 search --onlyvisible --name "Sandbox Status"
222 ''}"
223 )
224 )
225
226 clipboard = machine.succeed(
227 ru(
228 "echo void | ${pkgs.xclip}/bin/xclip -i >&2"
229 )
230 )
231 machine.succeed(
232 ru(
233 "${xdo "copy-sandbox-info" ''
234 key --delay 1000 Ctrl+a Ctrl+c
235 ''}"
236 )
237 )
238
239 clipboard = machine.succeed(
240 ru("${pkgs.xclip}/bin/xclip -o")
241 )
242 if not all(
243 re.search(filter, clipboard, flags=re.DOTALL | re.IGNORECASE)
244 for filter in filters
245 ):
246 assert False, f"copying twice in a row does not work properly: {clipboard}"
247
248 machine.screenshot("after_copy_from_chromium")
249
250
251 with test_new_win("gpu_info", "chrome://gpu", "GPU Internals"):
252 # To check the text rendering (catches regressions like #131074):
253 machine.wait_for_text("Graphics Feature Status")
254 # TODO: Fix copying all of the text to the clipboard
255
256
257 with test_new_win("version_info", "chrome://version", "About Version") as clipboard:
258 filters = [
259 r"${chromiumPkg.version} \(Official Build",
260 ]
261 if not all(
262 re.search(filter, clipboard) for filter in filters
263 ):
264 assert False, "Version info not correct."
265
266
267 machine.shutdown()
268 '';
269}) channelMap