summaryrefslogtreecommitdiffstats
path: root/nixos/tests/mtp.nix
blob: 47cf10561eb6a2210cc69495979b7433a90730ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
{ pkgs, ... }:
{
  name = "mtp";
  meta = with pkgs.lib.maintainers; {
    maintainers = [
      matthewcroughan
    ];
  };

  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
          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}
        '';
      };
    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")
    '';
}