lol

nixos/tests/mtp: init

Adds a fully fledged NixOS VM integration test which uses jmtpfs and
gvfs to test the functionality of MTP inside of NixOS. It uses USB
device emulation in QEMU to create MTP device(s) which can be tested
against.

+110
+1
nixos/tests/all-tests.nix
··· 315 315 moosefs = handleTest ./moosefs.nix {}; 316 316 mpd = handleTest ./mpd.nix {}; 317 317 mpv = handleTest ./mpv.nix {}; 318 + mtp = handleTest ./mtp.nix {}; 318 319 mumble = handleTest ./mumble.nix {}; 319 320 musescore = handleTest ./musescore.nix {}; 320 321 munin = handleTest ./munin.nix {};
+109
nixos/tests/mtp.nix
··· 1 + import ./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 + })