flatpak: the package, Flathub with it, and the installations as tools (hq to-be 42 phase 2.9)
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.
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
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])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user