package main import ( "strings" "testing" ) func TestTheManifestIsFlatpaksPackageAndFlathubComesWithIt(t *testing.T) { m := readManifest(t) holdsTheBundle(t, m, "flatpak") if got := strings.Join(m.packages(), ","); got != "flatpak" { t.Errorf("packages %s", got) } // The package ships flathub in /usr/share/flatpak/remotes.d: the module declares no remote file. if len(m.Resources) != 1 { t.Errorf("one resource: %v", m.Resources) } } const nb = " " // The desktop's installation on 2026-10-04, as flatpak list prints it. var apps = "com.nextcloud.desktopclient.nextcloud\tNextcloud Desktop\t3.14\tstable\tx86_64\tflathub\tsystem\t275.8" + nb + "MB\tcom.nextcloud.desktopclient.nextcloud/x86_64/stable\n" + "tv.plex.PlexDesktop\tPlex\t1.9\tstable\tx86_64\tflathub\tsystem\t368.0" + nb + "MB\ttv.plex.PlexDesktop/x86_64/stable\n" var runtimes = strings.Join([]string{ "org.freedesktop.Platform\tFreedesktop Platform\t22.08.1\t22.08\tx86_64\tflathub\tsystem\t576.7" + nb + "MB\torg.freedesktop.Platform/x86_64/22.08", "org.freedesktop.Platform\tFreedesktop Platform\t23.08.1\t23.08\tx86_64\tflathub\tsystem\t598.3" + nb + "MB\torg.freedesktop.Platform/x86_64/23.08", "org.freedesktop.Platform.GL.default\tMesa\t\t22.08\tx86_64\tflathub\tsystem\t442.1" + nb + "MB\torg.freedesktop.Platform.GL.default/x86_64/22.08", "org.freedesktop.Platform.GL.default\tMesa\t\t23.08\tx86_64\tflathub\tsystem\t533.8" + nb + "MB\torg.freedesktop.Platform.GL.default/x86_64/23.08", "org.freedesktop.Platform.GL.default\tMesa\t\t23.08-extra\tx86_64\tflathub\tsystem\t533.8" + nb + "MB\torg.freedesktop.Platform.GL.default/x86_64/23.08-extra", "org.freedesktop.Platform.openh264\topenh264\t\t2.2.0\tx86_64\tflathub\tsystem\t790.0" + nb + "kB\torg.freedesktop.Platform.openh264/x86_64/2.2.0", "org.freedesktop.Sdk\tSdk\t\t23.08\tx86_64\tflathub\tsystem\t1.6" + nb + "GB\torg.freedesktop.Sdk/x86_64/23.08", "org.kde.Platform\tKDE\t\t6.7\tx86_64\tflathub\tsystem\t931.4" + nb + "MB\torg.kde.Platform/x86_64/6.7", "org.kde.KStyle.Adwaita\tAdwaita\t\t5.15-21.08\tx86_64\tflathub\tsystem\t16.3" + nb + "MB\torg.kde.KStyle.Adwaita/x86_64/5.15-21.08", "org.kde.KStyle.Adwaita\tAdwaita\t\t6.7\tx86_64\tflathub\tsystem\t20.6" + nb + "MB\torg.kde.KStyle.Adwaita/x86_64/6.7", }, "\n") + "\n" const platformMeta = `[Runtime] name=org.freedesktop.Platform [Extension org.freedesktop.Platform.GL] versions = 23.08;23.08-extra;1.4 version = 1.4 directory = lib/x86_64-linux-gnu/GL subdirectories = true [Extension org.freedesktop.Platform.openh264] directory = lib/openh264 version = 2.2.0 [Extension org.freedesktop.Platform.Timezones] directory = share/zoneinfo ` const kdeMeta = `[Runtime] name=org.kde.Platform [Extension org.kde.KStyle] directory = lib/plugins/styles subdirectories = true version = 6.7 [Extension org.freedesktop.Platform.GL] versions = 23.08;1.4 subdirectories = true ` func theDesktop(t *testing.T) *fake { return using(t, func(line string, c Cmd) Result { switch { case strings.HasPrefix(line, "flatpak list --app"): return ok(apps) case strings.HasPrefix(line, "flatpak list --runtime"): return ok(runtimes) case strings.Contains(line, "--show-runtime com.nextcloud"): return ok("org.kde.Platform/x86_64/6.7\n") case strings.Contains(line, "--show-runtime tv.plex"): return ok("org.freedesktop.Platform/x86_64/23.08\n") case strings.Contains(line, "--show-sdk tv.plex"): return ok("org.freedesktop.Sdk/x86_64/23.08\n") case strings.Contains(line, "--show-sdk"): return ok("org.kde.Sdk/x86_64/6.7\n") case strings.Contains(line, "--show-metadata org.freedesktop.Platform/x86_64/23.08"): return ok(platformMeta) case strings.Contains(line, "--show-metadata org.kde.Platform"): return ok(kdeMeta) case strings.Contains(line, "--show-metadata"): return ok("[Application]\nname=x\n") case strings.HasPrefix(line, "flatpak pin"): return ok("") case strings.HasPrefix(line, "flatpak remotes"): return ok("flathub\thttps://dl.flathub.org/repo/\tsystem\t1\nflathub\thttps://dl.flathub.org/repo/\tuser\t1\n") } return Result{Status: 9, Stderr: "unexpected " + line} }) } func TestUnusedIsWhatNoApplicationNeedsDirectlyOrThroughAnExtensionPoint(t *testing.T) { f := theDesktop(t) got, err := Unused() if err != nil { t.Fatal(err) } names := []string{} for _, r := range got.Unused { names = append(names, r.Ref) } want := "org.freedesktop.Platform.GL.default/x86_64/22.08,org.freedesktop.Platform/x86_64/22.08,org.kde.KStyle.Adwaita/x86_64/5.15-21.08" if strings.Join(names, ",") != want { t.Errorf("unused\n%s\nwant\n%s", strings.Join(names, ","), want) } if got.Bytes != 576700000+442100000+16300000 { t.Errorf("bytes %d", got.Bytes) } for _, l := range f.lines() { if strings.Contains(l, "uninstall") || strings.HasPrefix(l, "sudo") { t.Errorf("unused only reads: %s", l) } } } func TestAnExtensionPointAcceptsItsVersionsOrTheDeclaringBranch(t *testing.T) { ext := extensionsOf(platformMeta, "23.08") if len(ext) != 3 { t.Fatalf("%+v", ext) } gl, h264, tz := ext[0], ext[1], ext[2] if !gl.Subdirs || strings.Join(gl.Versions, ";") != "23.08;23.08-extra;1.4" { t.Errorf("versions win over version: %+v", gl) } if h264.Subdirs || strings.Join(h264.Versions, ";") != "2.2.0" { t.Errorf("%+v", h264) } if strings.Join(tz.Versions, ";") != "23.08" { t.Errorf("no version is the declaring branch: %+v", tz) } if !gl.fills(Ref{ID: "org.freedesktop.Platform.GL.default", Branch: "23.08-extra"}) || gl.fills(Ref{ID: "org.freedesktop.Platform.GL.default", Branch: "22.08"}) { t.Error("a subdirectory extension, by branch") } if h264.fills(Ref{ID: "org.freedesktop.Platform.openh264.x", Branch: "2.2.0"}) { t.Error("without subdirectories only the id itself") } } func TestRemotesFindTheDuplicateAndTheEmptyOne(t *testing.T) { theDesktop(t) got, err := Remotes() if err != nil || len(got.Remotes) != 2 || !got.Flathub { t.Fatalf("%+v %v", got, err) } if got.Remotes[0].Installed != 12 || got.Remotes[1].Installed != 0 { t.Errorf("%+v", got.Remotes) } all := strings.Join(got.Findings, ";") if !strings.Contains(all, "flathub in the user installation has nothing installed") || !strings.Contains(all, "configured 2 times") { t.Errorf("%s", all) } } func TestListReadsSizesAsBytes(t *testing.T) { theDesktop(t) got, err := List("app", "") if err != nil || got.Count != 2 || got.Refs[0].Bytes != 275800000 || got.Refs[0].Size != "275.8 MB" || got.Bytes != 643800000 { t.Fatalf("%+v %v", got, err) } for in, want := range map[string]int64{"1.6" + nb + "GB": 1600000000, "790.0 kB": 790000, "12 bytes": 12, "": 0} { if b := sizeBytes(in); b != want { t.Errorf("%q: %d", in, b) } } } func TestActsOnTheSystemInstallationEscalateAndTheAccountsDoNot(t *testing.T) { f := using(t, func(string, Cmd) Result { return ok("done") }) if got, err := Install("org.gimp.GIMP", "flathub", "system"); err != nil || got.Job.Running { t.Fatalf("%+v %v", got, err) } if _, err := Install("org.gimp.GIMP", "flathub", "user"); err != nil { t.Fatal(err) } if _, err := Remove("org.gimp.GIMP", "system", true); err != nil { t.Fatal(err) } if _, err := Update("", "user"); err != nil { t.Fatal(err) } if _, err := RemoveUnused("system"); err != nil { t.Fatal(err) } want := []string{ "sudo -n flatpak install --system --noninteractive -y flathub org.gimp.GIMP", "flatpak install --user --noninteractive -y flathub org.gimp.GIMP", "sudo -n flatpak uninstall --system --noninteractive -y --delete-data org.gimp.GIMP", "flatpak update --user --noninteractive -y", "sudo -n flatpak uninstall --system --noninteractive -y --unused", } if got := strings.Join(f.lines(), "\n"); got != strings.Join(want, "\n") { t.Errorf("asked\n%s", got) } if _, err := Install("org.gimp.GIMP", "--from=x", "system"); err == nil { t.Error("an option as a remote") } for _, bad := range []string{"--assumeyes", "gimp", "org.gimp.GIMP; rm", "org/../x"} { if checkRef(bad) == nil { t.Errorf("%q accepted as a ref", bad) } } for _, good := range []string{"org.gimp.GIMP", "org.freedesktop.Platform/x86_64/23.08", "app/org.gimp.GIMP/x86_64/stable", "org.freedesktop.Platform.GL.default//23.08-extra"} { if err := checkRef(good); err != nil { t.Errorf("%v", err) } } } func TestAFailedInstallIsAnErrorAndAMissingFlatpakIsSaid(t *testing.T) { using(t, func(string, Cmd) Result { return Result{Status: 1, Stderr: "error: Nothing matches org.nope.Nope in remote flathub"} }) if _, err := Install("org.nope.Nope", "flathub", "user"); err == nil || !strings.Contains(err.Error(), "Nothing matches") { t.Fatalf("%v", err) } using(t, func(string, Cmd) Result { return Result{Status: 127, Error: "not-found"} }) if _, err := List("app", ""); err == nil || !strings.Contains(err.Error(), "not installed") { t.Fatalf("%v", err) } } func TestDiskUsageReadsEachInstallationAndToleratesAnUnreadableFile(t *testing.T) { t.Setenv("MESH_OPERATOR_HOME", "/home/op") using(t, func(line string, c Cmd) Result { switch { case line == "du -sb /var/lib/flatpak": return Result{Status: 1, Stdout: "5094728394\t/var/lib/flatpak\n", Stderr: "du: cannot read directory 'x': Permission denied"} case line == "du -sb /home/op/.local/share/flatpak": return Result{Status: 1, Stderr: "du: cannot access '/home/op/.local/share/flatpak': No such file or directory"} case strings.Contains(line, "--app"): return ok(apps) } return ok(runtimes) }) got, err := DiskUsage() if err != nil || got.Installations["system"] != 5094728394 || got.Installations["user"] != 0 || got.Note == "" { t.Fatalf("%+v %v", got, err) } if got.Largest[0].ID != "org.freedesktop.Sdk" { t.Errorf("largest first: %+v", got.Largest[0]) } }