The workstations' XDG conventions had no owner: xdg-user-dirs-update rewrote the folders file at every login, both machines' default-application lists named an editor neither has, and the laptop kept two dead links of the retired predecessor. The module adopts the operator's folders (three as settings, as the machines differ), disables the update so it cannot undo the mesh's file, and writes the machine's own mimeapps list, the last one read, so the person's choices in their own list always win. Autostart is listed, not owned: each entry is its application's module's.
197 lines
6.6 KiB
Go
197 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// xdg's shape: it claims no seat (the conventions are not a role anything calls), needs no
|
|
// display, owns the account's folders file whole and the machine's lowest list of default
|
|
// applications, and holds the person's own list of defaults and every autostart entry as found.
|
|
|
|
type manifest struct {
|
|
Module string `json:"module"`
|
|
Capabilities []string `json:"capabilities"`
|
|
Requires []string `json:"requires"`
|
|
Claims []any `json:"claims"`
|
|
Tools []string `json:"tools"`
|
|
Shell []any `json:"shell"`
|
|
Contributions []any `json:"contributions"`
|
|
Resources []map[string]any `json:"resources"`
|
|
Build struct {
|
|
Artifacts []map[string]any `json:"artifacts"`
|
|
} `json:"build"`
|
|
}
|
|
|
|
func readManifest(t *testing.T) (manifest, string) {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var m manifest
|
|
if err := json.Unmarshal(raw, &m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return m, string(raw)
|
|
}
|
|
|
|
func TestItClaimsNoSeatNeedsNoDisplayAndContributesNothing(t *testing.T) {
|
|
m, _ := readManifest(t)
|
|
if m.Module != "xdg" || m.Claims != nil || m.Requires != nil || m.Shell != nil || m.Contributions != nil {
|
|
t.Fatalf("%+v", m)
|
|
}
|
|
if !reflect.DeepEqual(m.Capabilities, []string{"package-manager"}) {
|
|
t.Fatalf("%v", m.Capabilities)
|
|
}
|
|
}
|
|
|
|
func TestItOwnsTheFilesItsSourcesHold(t *testing.T) {
|
|
m, _ := readManifest(t)
|
|
want := map[string][3]string{
|
|
"user-dirs": {"${machine:account-home}/.config/user-dirs.dirs", "0600", "user-dirs.dirs"},
|
|
"user-dirs-locale": {"${machine:account-home}/.config/user-dirs.locale", "0644", "user-dirs.locale"},
|
|
"user-dirs-update": {"${machine:account-home}/.config/user-dirs.conf", "0644", "user-dirs.conf"},
|
|
"default-applications": {MeshList, "0644", "mimeapps.list"},
|
|
}
|
|
var pkgs []string
|
|
files := 0
|
|
for _, r := range m.Resources {
|
|
id, _ := r["id"].(string)
|
|
if r["type"] == "package" {
|
|
pkgs = append(pkgs, r["package"].(string))
|
|
continue
|
|
}
|
|
w, ok := want[id]
|
|
if !ok || r["type"] != "file" {
|
|
t.Errorf("unexpected resource %v", r)
|
|
continue
|
|
}
|
|
files++
|
|
src, _ := os.ReadFile(filepath.Join("..", "..", "config", w[2]))
|
|
if r["path"] != w[0] || r["mode"] != w[1] || r["content"] != string(src) || r["into"] != nil {
|
|
t.Errorf("%s is not config/%s, whole, at %s mode %s: %v", id, w[2], w[0], w[1], r["path"])
|
|
}
|
|
home := strings.HasPrefix(w[0], "${machine:account-home}/")
|
|
if home != (r["owner"] == "${machine:account}") {
|
|
t.Errorf("%s: a file in the home is the account's, a system file root's: owner %v", id, r["owner"])
|
|
}
|
|
}
|
|
if files != len(want) {
|
|
t.Errorf("%d files, want %d", files, len(want))
|
|
}
|
|
if !reflect.DeepEqual(pkgs, []string{"xdg-user-dirs", "xdg-utils"}) {
|
|
t.Errorf("packages %v", pkgs)
|
|
}
|
|
for _, r := range m.Resources {
|
|
if p, _ := r["path"].(string); strings.Contains(p, "mimeapps.list") && strings.Contains(p, "account-home") {
|
|
t.Errorf("the person's mimeapps.list is held as found, never written: %v", r)
|
|
}
|
|
if p, _ := r["path"].(string); strings.Contains(p, "autostart") || strings.Contains(p, "xdg-desktop-portal") {
|
|
t.Errorf("autostart entries are their applications' modules', the portal is adwaita's: %v", r)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheFoldersFileIsXdgUserDirsOwnFormatWithThreeSettings(t *testing.T) {
|
|
raw, _ := os.ReadFile(filepath.Join("..", "..", "config", "user-dirs.dirs"))
|
|
text := string(raw)
|
|
settings := regexp.MustCompile(`\$\{setting:([a-z0-9-]+)\}`).FindAllStringSubmatch(text, -1)
|
|
var names []string
|
|
for _, s := range settings {
|
|
names = append(names, s[1])
|
|
}
|
|
sort.Strings(names)
|
|
if !reflect.DeepEqual(names, []string{"desktop-dir", "publicshare-dir", "videos-dir"}) {
|
|
t.Fatalf("the folders that differ between the workstations are the settings: %v", names)
|
|
}
|
|
lines := 0
|
|
for _, l := range strings.Split(strings.TrimSpace(text), "\n") {
|
|
if strings.HasPrefix(l, "#") {
|
|
continue
|
|
}
|
|
lines++
|
|
if !userDirLine.MatchString(l) {
|
|
t.Errorf("not xdg-user-dirs' format: %q", l)
|
|
}
|
|
}
|
|
// The eight folders GLib knows, and xdg-user-dirs' PROJECTS.
|
|
if lines != 9 {
|
|
t.Errorf("%d folders", lines)
|
|
}
|
|
if !strings.Contains(firstLines(text, 3), meshMark) {
|
|
t.Error("the header xdg_check recognises the file by")
|
|
}
|
|
conf, _ := os.ReadFile(filepath.Join("..", "..", "config", "user-dirs.conf"))
|
|
if !regexp.MustCompile(`(?m)^enabled=False$`).Match(conf) {
|
|
t.Error("xdg-user-dirs-update must be disabled, or it rewrites the mesh's file at login")
|
|
}
|
|
}
|
|
|
|
func TestTheMeshsDefaultsNameEachTypeOnceInOneGroupEach(t *testing.T) {
|
|
raw, _ := os.ReadFile(filepath.Join("..", "..", "config", "mimeapps.list"))
|
|
k := ParseKeyFile(string(raw))
|
|
if len(k.DupGroups)+len(k.DupKeys)+len(k.Unparsable) != 0 {
|
|
t.Fatalf("%+v", k)
|
|
}
|
|
if !reflect.DeepEqual(k.Order, []string{"Default Applications", "Added Associations"}) {
|
|
t.Fatalf("%v", k.Order)
|
|
}
|
|
for typ, v := range k.Groups["Default Applications"] {
|
|
if _, err := Normalise(typ); err != nil {
|
|
t.Errorf("%s: %v", typ, err)
|
|
}
|
|
for _, id := range List(v) {
|
|
if !desktopID.MatchString(id) || regexp.MustCompile(`[0-9a-f]{8}-[0-9a-f]{4}-`).MatchString(id) {
|
|
t.Errorf("%s=%s: an id one machine generated is not a default for every machine", typ, id)
|
|
}
|
|
if id == "code-oss.desktop" {
|
|
t.Errorf("%s: code-oss is installed on neither workstation", typ)
|
|
}
|
|
}
|
|
}
|
|
if k.Groups["Default Applications"]["x-scheme-handler/https"] != "firefox.desktop;" {
|
|
t.Error("the browser is the operator's")
|
|
}
|
|
}
|
|
|
|
func TestTheToolsAgreeWithTheManifest(t *testing.T) {
|
|
m, raw := readManifest(t)
|
|
served := map[string]bool{}
|
|
for _, tool := range tools(&Machine{}) {
|
|
served[tool.Name] = true
|
|
if !strings.HasPrefix(tool.Name, "xdg_") || strings.TrimSpace(tool.Description) == "" {
|
|
t.Errorf("%s", tool.Name)
|
|
}
|
|
}
|
|
if len(served) != len(m.Tools) {
|
|
t.Fatalf("%v %v", served, m.Tools)
|
|
}
|
|
for _, n := range m.Tools {
|
|
if !served[n] {
|
|
t.Errorf("module.json lists %s, which the bundle does not serve", n)
|
|
}
|
|
}
|
|
var bundle map[string]any
|
|
for _, a := range m.Build.Artifacts {
|
|
if a["kind"] == "bundle" {
|
|
bundle = a
|
|
}
|
|
}
|
|
if bundle == nil || bundle["language"] != "go" || bundle["system"] != "arch" || bundle["from"] != "cmd/xdg-tools" || bundle["binary"] != "xdg-tools" {
|
|
t.Errorf("the Go tools bundle: %v", bundle)
|
|
}
|
|
s := strings.ToLower(raw)
|
|
for _, never := range []string{"/home/", "jochen", "g14", "shanks", "novox.be", "zurag", ".hal/", "api_key"} {
|
|
if strings.Contains(s, never) {
|
|
t.Errorf("module.json names %q", never)
|
|
}
|
|
}
|
|
}
|