mesh/merge-gate pass: builds xdg → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
user-dirs.dirs is shell assignments meant to be sourced by a shell, and a setting is pasted into it as it is, so a folder of $HOME/x"; <command>; X=" set through the verb would add a command of the caller's. With the file trusted again, the three folders are set at the controller's terminal alone until settings are typed and quoted at render.
215 lines
7.3 KiB
Go
215 lines
7.3 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The folders file stays trusted (unmarked), so its three settings are set at the controller's terminal
|
|
// alone. A value is pasted into it as it is, and the format is shell assignments defined to be sourced
|
|
// by a shell: `$HOME/x"; <command>; X="` would add a command of the caller's (hq issue 346). Until
|
|
// settings are typed and quoted at render, no verb may set them.
|
|
func TestTheFoldersFileStaysTrustedUntilSettingsAreQuoted(t *testing.T) {
|
|
m, _ := readManifest(t)
|
|
for _, r := range m.Resources {
|
|
if r["id"] != "user-dirs" {
|
|
continue
|
|
}
|
|
if _, marked := r["trusted"]; marked {
|
|
t.Fatal("user-dirs.dirs carries a trusted marking: its settings are the terminal's alone (hq issue 346)")
|
|
}
|
|
return
|
|
}
|
|
t.Fatal("no user-dirs resource")
|
|
}
|