The package ships Flathub in /usr/share/flatpak/remotes.d, so the module declares no remote of its own and checks it instead. Eleven Go tools: list, runtimes, remotes (naming the desktop's duplicate user Flathub), updates, unused, disk usage, and install, remove, update and remove-unused, the system installation's acts through sudo -n and the account's without. uninstall --unused has no dry run, so flatpak_unused works it out from flatpak's own answers: an application's runtime and SDK, the extension points of what is used, and pins. Acts run as jobs inside the bundle, because an install outlasts a call.
250 lines
9.6 KiB
Go
250 lines
9.6 KiB
Go
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])
|
||
}
|
||
}
|