1# Test printing via CUPS.
2
3import ./make-test-python.nix (
4{ pkgs
5, socket ? true # whether to use socket activation
6, ...
7}:
8
9{
10 name = "printing";
11 meta = with pkgs.lib.maintainers; {
12 maintainers = [ domenkozar eelco matthewbauer ];
13 };
14
15 nodes.server = { ... }: {
16 services.printing = {
17 enable = true;
18 stateless = true;
19 startWhenNeeded = socket;
20 listenAddresses = [ "*:631" ];
21 defaultShared = true;
22 extraConf = ''
23 <Location />
24 Order allow,deny
25 Allow from all
26 </Location>
27 '';
28 };
29 networking.firewall.allowedTCPPorts = [ 631 ];
30 # Add a HP Deskjet printer connected via USB to the server.
31 hardware.printers.ensurePrinters = [{
32 name = "DeskjetLocal";
33 deviceUri = "usb://foobar/printers/foobar";
34 model = "drv:///sample.drv/deskjet.ppd";
35 }];
36 };
37
38 nodes.client = { ... }: {
39 services.printing.enable = true;
40 services.printing.startWhenNeeded = socket;
41 # Add printer to the client as well, via IPP.
42 hardware.printers.ensurePrinters = [{
43 name = "DeskjetRemote";
44 deviceUri = "ipp://server/printers/DeskjetLocal";
45 model = "drv:///sample.drv/deskjet.ppd";
46 }];
47 hardware.printers.ensureDefaultPrinter = "DeskjetRemote";
48 };
49
50 testScript = ''
51 import os
52 import re
53
54 start_all()
55
56 with subtest("Make sure that cups is up on both sides and printers are set up"):
57 server.wait_for_unit("cups.${if socket then "socket" else "service"}")
58 client.wait_for_unit("cups.${if socket then "socket" else "service"}")
59
60 assert "scheduler is running" in client.succeed("lpstat -r")
61
62 with subtest("UNIX socket is used for connections"):
63 assert "/var/run/cups/cups.sock" in client.succeed("lpstat -H")
64
65 with subtest("HTTP server is available too"):
66 client.succeed("curl --fail http://localhost:631/")
67 client.succeed(f"curl --fail http://{server.name}:631/")
68 server.fail(f"curl --fail --connect-timeout 2 http://{client.name}:631/")
69
70 with subtest("LP status checks"):
71 assert "DeskjetRemote accepting requests" in client.succeed("lpstat -a")
72 assert "DeskjetLocal accepting requests" in client.succeed(
73 f"lpstat -h {server.name}:631 -a"
74 )
75 client.succeed("cupsdisable DeskjetRemote")
76 out = client.succeed("lpq")
77 print(out)
78 assert re.search(
79 "DeskjetRemote is not ready.*no entries",
80 client.succeed("lpq"),
81 flags=re.DOTALL,
82 )
83 client.succeed("cupsenable DeskjetRemote")
84 assert re.match(
85 "DeskjetRemote is ready.*no entries", client.succeed("lpq"), flags=re.DOTALL
86 )
87
88 # Test printing various file types.
89 for file in [
90 "${pkgs.groff.doc}/share/doc/*/examples/mom/penguin.pdf",
91 "${pkgs.groff.doc}/share/doc/*/meref.ps",
92 "${pkgs.cups.out}/share/doc/cups/images/cups.png",
93 "${pkgs.pcre.doc}/share/doc/pcre/pcre.txt",
94 ]:
95 file_name = os.path.basename(file)
96 with subtest(f"print {file_name}"):
97 # Print the file on the client.
98 print(client.succeed("lpq"))
99 client.succeed(f"lp {file}")
100 client.wait_until_succeeds(
101 f"lpq; lpq | grep -q -E 'active.*root.*{file_name}'"
102 )
103
104 # Ensure that a raw PCL file appeared in the server's queue
105 # (showing that the right filters have been applied). Of
106 # course, since there is no actual USB printer attached, the
107 # file will stay in the queue forever.
108 server.wait_for_file("/var/spool/cups/d*-001")
109 server.wait_until_succeeds(f"lpq -a | grep -q -E '{file_name}'")
110
111 # Delete the job on the client. It should disappear on the
112 # server as well.
113 client.succeed("lprm")
114 client.wait_until_succeeds("lpq -a | grep -q -E 'no entries'")
115
116 retry(lambda _: "no entries" in server.succeed("lpq -a"))
117
118 # The queue is empty already, so this should be safe.
119 # Otherwise, pairs of "c*"-"d*-001" files might persist.
120 server.execute("rm /var/spool/cups/*")
121 '';
122})