import ./make-test-python.nix ({ pkgs, ... }: { name = "mtp"; meta = with pkgs.lib.maintainers; { maintainers = [ matthewcroughan nixinator ]; }; nodes = { client = { config, pkgs, ... }: { # DBUS runs only once a user session is created, which means a user has to # login. Here, we log in as root. Once logged in, the gvfs-daemon service runs # as UID 0 in User-0.service services.getty.autologinUser = "root"; # XDG_RUNTIME_DIR is needed for running systemd-user services such as # gvfs-daemon as root. environment.variables.XDG_RUNTIME_DIR = "/run/user/0"; environment.systemPackages = with pkgs; [ usbutils glib jmtpfs tree ]; services.gvfs.enable = true; # Creates a usb-mtp device inside the VM, which is mapped to the host's # /tmp folder, it is able to write files to this location, but only has # permissions to read its own creations. virtualisation.qemu.options = [ "-usb" "-device usb-mtp,rootdir=/tmp,readonly=false" ]; }; }; testScript = { nodes, ... }: let # Creates a list of QEMU MTP devices matching USB ID (46f4:0004). This # value can be sourced in a shell script. This is so we can loop over the # devices we find, as this test may want to use more than one MTP device # in future. mtpDevices = pkgs.writeScript "mtpDevices.sh" '' export mtpDevices=$(lsusb -d 46f4:0004 | awk {'print $2","$4'} | sed 's/[:-]/ /g') ''; # Qemu is only capable of creating an MTP device with Picture Transfer # Protocol. This means that gvfs must use gphoto2:// rather than mtp:// # when mounting. # https://github.com/qemu/qemu/blob/970bc16f60937bcfd334f14c614bd4407c247961/hw/usb/dev-mtp.c#L278 gvfs = rec { mountAllMtpDevices = pkgs.writeScript "mountAllMtpDevices.sh" '' set -e source ${mtpDevices} for i in $mtpDevices do gio mount "gphoto2://[usb:$i]/" done ''; unmountAllMtpDevices = pkgs.writeScript "unmountAllMtpDevices.sh" '' set -e source ${mtpDevices} for i in $mtpDevices do gio mount -u "gphoto2://[usb:$i]/" done ''; # gvfsTest: # 1. Creates a 10M test file # 2. Copies it to the device using GIO tools # 3. Checks for corruption with `diff` # 4. Removes the file, then unmounts the disks. gvfsTest = pkgs.writeScript "gvfsTest.sh" '' set -e source ${mtpDevices} ${mountAllMtpDevices} dd if=/dev/urandom of=testFile10M bs=1M count=10 for i in $mtpDevices do gio copy ./testFile10M gphoto2://[usb:$i]/ ls -lah /run/user/0/gvfs/*/testFile10M gio remove gphoto2://[usb:$i]/testFile10M done ${unmountAllMtpDevices} ''; }; jmtpfs = { # jmtpfsTest: # 1. Mounts the device on a dir named `phone` using jmtpfs # 2. Puts the current Nixpkgs libmtp version into a file # 3. Checks for corruption with `diff` # 4. Prints the directory tree jmtpfsTest = pkgs.writeScript "jmtpfsTest.sh" '' mkdir phone jmtpfs phone echo "${pkgs.libmtp.version}" > phone/tmp/testFile echo "${pkgs.libmtp.version}" > testFile diff phone/tmp/testFile testFile tree phone ''; }; in # Using >&2 allows the results of the scripts to be printed to the terminal # when building this test with Nix. Scripts would otherwise complete # silently. '' start_all() client.wait_for_unit("multi-user.target") client.wait_for_unit("dbus.service") client.succeed("${gvfs.gvfsTest} >&2") client.succeed("${jmtpfs.jmtpfsTest} >&2") ''; })