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.
···11+import ./make-test-python.nix ({ pkgs, ... }: {
22+ name = "mtp";
33+ meta = with pkgs.lib.maintainers; {
44+ maintainers = [ matthewcroughan nixinator ];
55+ };
66+77+ nodes =
88+ {
99+ client = { config, pkgs, ... }: {
1010+ # DBUS runs only once a user session is created, which means a user has to
1111+ # login. Here, we log in as root. Once logged in, the gvfs-daemon service runs
1212+ # as UID 0 in User-0.service
1313+ services.getty.autologinUser = "root";
1414+1515+ # XDG_RUNTIME_DIR is needed for running systemd-user services such as
1616+ # gvfs-daemon as root.
1717+ environment.variables.XDG_RUNTIME_DIR = "/run/user/0";
1818+1919+ environment.systemPackages = with pkgs; [ usbutils glib jmtpfs tree ];
2020+ services.gvfs.enable = true;
2121+2222+ # Creates a usb-mtp device inside the VM, which is mapped to the host's
2323+ # /tmp folder, it is able to write files to this location, but only has
2424+ # permissions to read its own creations.
2525+ virtualisation.qemu.options = [
2626+ "-usb"
2727+ "-device usb-mtp,rootdir=/tmp,readonly=false"
2828+ ];
2929+ };
3030+ };
3131+3232+3333+ testScript = { nodes, ... }:
3434+ let
3535+ # Creates a list of QEMU MTP devices matching USB ID (46f4:0004). This
3636+ # value can be sourced in a shell script. This is so we can loop over the
3737+ # devices we find, as this test may want to use more than one MTP device
3838+ # in future.
3939+ mtpDevices = pkgs.writeScript "mtpDevices.sh" ''
4040+ export mtpDevices=$(lsusb -d 46f4:0004 | awk {'print $2","$4'} | sed 's/[:-]/ /g')
4141+ '';
4242+ # Qemu is only capable of creating an MTP device with Picture Transfer
4343+ # Protocol. This means that gvfs must use gphoto2:// rather than mtp://
4444+ # when mounting.
4545+ # https://github.com/qemu/qemu/blob/970bc16f60937bcfd334f14c614bd4407c247961/hw/usb/dev-mtp.c#L278
4646+ gvfs = rec {
4747+ mountAllMtpDevices = pkgs.writeScript "mountAllMtpDevices.sh" ''
4848+ set -e
4949+ source ${mtpDevices}
5050+ for i in $mtpDevices
5151+ do
5252+ gio mount "gphoto2://[usb:$i]/"
5353+ done
5454+ '';
5555+ unmountAllMtpDevices = pkgs.writeScript "unmountAllMtpDevices.sh" ''
5656+ set -e
5757+ source ${mtpDevices}
5858+ for i in $mtpDevices
5959+ do
6060+ gio mount -u "gphoto2://[usb:$i]/"
6161+ done
6262+ '';
6363+ # gvfsTest:
6464+ # 1. Creates a 10M test file
6565+ # 2. Copies it to the device using GIO tools
6666+ # 3. Checks for corruption with `diff`
6767+ # 4. Removes the file, then unmounts the disks.
6868+ gvfsTest = pkgs.writeScript "gvfsTest.sh" ''
6969+ set -e
7070+ source ${mtpDevices}
7171+ ${mountAllMtpDevices}
7272+ dd if=/dev/urandom of=testFile10M bs=1M count=10
7373+ for i in $mtpDevices
7474+ do
7575+ gio copy ./testFile10M gphoto2://[usb:$i]/
7676+ ls -lah /run/user/0/gvfs/*/testFile10M
7777+ gio remove gphoto2://[usb:$i]/testFile10M
7878+ done
7979+ ${unmountAllMtpDevices}
8080+ '';
8181+ };
8282+ jmtpfs = {
8383+ # jmtpfsTest:
8484+ # 1. Mounts the device on a dir named `phone` using jmtpfs
8585+ # 2. Puts the current Nixpkgs libmtp version into a file
8686+ # 3. Checks for corruption with `diff`
8787+ # 4. Prints the directory tree
8888+ jmtpfsTest = pkgs.writeScript "jmtpfsTest.sh" ''
8989+ set -e
9090+ mkdir phone
9191+ jmtpfs phone
9292+ echo "${pkgs.libmtp.version}" > phone/tmp/testFile
9393+ echo "${pkgs.libmtp.version}" > testFile
9494+ diff phone/tmp/testFile testFile
9595+ tree phone
9696+ '';
9797+ };
9898+ in
9999+ # Using >&2 allows the results of the scripts to be printed to the terminal
100100+ # when building this test with Nix. Scripts would otherwise complete
101101+ # silently.
102102+ ''
103103+ start_all()
104104+ client.wait_for_unit("multi-user.target")
105105+ client.wait_for_unit("dbus.service")
106106+ client.succeed("${gvfs.gvfsTest} >&2")
107107+ client.succeed("${jmtpfs.jmtpfsTest} >&2")
108108+ '';
109109+})