1import ./make-test-python.nix (
2 { pkgs, ... }:
3 {
4 name = "mtp";
5 meta = with pkgs.lib.maintainers; {
6 maintainers = [
7 matthewcroughan
8 nixinator
9 ];
10 };
11
12 nodes = {
13 client =
14 { config, pkgs, ... }:
15 {
16 # DBUS runs only once a user session is created, which means a user has to
17 # login. Here, we log in as root. Once logged in, the gvfs-daemon service runs
18 # as UID 0 in User-0.service
19 services.getty.autologinUser = "root";
20
21 # XDG_RUNTIME_DIR is needed for running systemd-user services such as
22 # gvfs-daemon as root.
23 environment.variables.XDG_RUNTIME_DIR = "/run/user/0";
24
25 environment.systemPackages = with pkgs; [
26 usbutils
27 glib
28 jmtpfs
29 tree
30 ];
31 services.gvfs.enable = true;
32
33 # Creates a usb-mtp device inside the VM, which is mapped to the host's
34 # /tmp folder, it is able to write files to this location, but only has
35 # permissions to read its own creations.
36 virtualisation.qemu.options = [
37 "-usb"
38 "-device usb-mtp,rootdir=/tmp,readonly=false"
39 ];
40 };
41 };
42
43 testScript =
44 { nodes, ... }:
45 let
46 # Creates a list of QEMU MTP devices matching USB ID (46f4:0004). This
47 # value can be sourced in a shell script. This is so we can loop over the
48 # devices we find, as this test may want to use more than one MTP device
49 # in future.
50 mtpDevices = pkgs.writeScript "mtpDevices.sh" ''
51 export mtpDevices=$(lsusb -d 46f4:0004 | awk {'print $2","$4'} | sed 's/[:-]/ /g')
52 '';
53 # Qemu is only capable of creating an MTP device with Picture Transfer
54 # Protocol. This means that gvfs must use gphoto2:// rather than mtp://
55 # when mounting.
56 # https://github.com/qemu/qemu/blob/970bc16f60937bcfd334f14c614bd4407c247961/hw/usb/dev-mtp.c#L278
57 gvfs = rec {
58 mountAllMtpDevices = pkgs.writeScript "mountAllMtpDevices.sh" ''
59 set -e
60 source ${mtpDevices}
61 for i in $mtpDevices
62 do
63 gio mount "gphoto2://[usb:$i]/"
64 done
65 '';
66 unmountAllMtpDevices = pkgs.writeScript "unmountAllMtpDevices.sh" ''
67 set -e
68 source ${mtpDevices}
69 for i in $mtpDevices
70 do
71 gio mount -u "gphoto2://[usb:$i]/"
72 done
73 '';
74 # gvfsTest:
75 # 1. Creates a 10M test file
76 # 2. Copies it to the device using GIO tools
77 # 3. Checks for corruption with `diff`
78 # 4. Removes the file, then unmounts the disks.
79 gvfsTest = pkgs.writeScript "gvfsTest.sh" ''
80 set -e
81 source ${mtpDevices}
82 ${mountAllMtpDevices}
83 dd if=/dev/urandom of=testFile10M bs=1M count=10
84 for i in $mtpDevices
85 do
86 gio copy ./testFile10M gphoto2://[usb:$i]/
87 ls -lah /run/user/0/gvfs/*/testFile10M
88 gio remove gphoto2://[usb:$i]/testFile10M
89 done
90 ${unmountAllMtpDevices}
91 '';
92 };
93 jmtpfs = {
94 # jmtpfsTest:
95 # 1. Mounts the device on a dir named `phone` using jmtpfs
96 # 2. Puts the current Nixpkgs libmtp version into a file
97 # 3. Checks for corruption with `diff`
98 # 4. Prints the directory tree
99 jmtpfsTest = pkgs.writeScript "jmtpfsTest.sh" ''
100 set -e
101 mkdir phone
102 jmtpfs phone
103 echo "${pkgs.libmtp.version}" > phone/tmp/testFile
104 echo "${pkgs.libmtp.version}" > testFile
105 diff phone/tmp/testFile testFile
106 tree phone
107 '';
108 };
109 in
110 # Using >&2 allows the results of the scripts to be printed to the terminal
111 # when building this test with Nix. Scripts would otherwise complete
112 # silently.
113 ''
114 start_all()
115 client.wait_for_unit("multi-user.target")
116 client.wait_for_unit("dbus.service")
117 client.succeed("${gvfs.gvfsTest} >&2")
118 client.succeed("${jmtpfs.jmtpfsTest} >&2")
119 '';
120 }
121)