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