mesh/merge-gate pass: builds picom → 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 head of the same pull request
A window mapped but left unpainted, or stale after a workspace switch, is a known failure of picom's glx backend with damage tracking on some drivers, and the first suspect for the empty calendar reminder on the workstation. Research 037 already found the backend to depend on the machine's driver. Both become preferences whose defaults are today's (glx, use-damage true), so nothing changes until a machine is given xrender or use-damage false to test it.
184 lines
5.2 KiB
Go
184 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The module's manifest, read the way the catalogue reads it, for the manifest tests. The same in
|
|
// every desktop module that carries it.
|
|
|
|
type manifest struct {
|
|
Module string `json:"module"`
|
|
Version string `json:"version"`
|
|
Capabilities []string `json:"capabilities"`
|
|
Requires []string `json:"requires"`
|
|
Claims []claim `json:"claims"`
|
|
Seats []any `json:"seats"`
|
|
Tools []string `json:"tools"`
|
|
Settings map[string]setting `json:"settings"`
|
|
Environment *environment `json:"environment"`
|
|
Shell []shellCode `json:"shell"`
|
|
Resources []map[string]any `json:"resources"`
|
|
Build struct {
|
|
Artifacts []map[string]any `json:"artifacts"`
|
|
} `json:"build"`
|
|
}
|
|
|
|
// setting is one key's default, as the definition gives it (novox/hq ADR 0262).
|
|
type setting struct {
|
|
Kind string `json:"kind"`
|
|
Default any `json:"default"`
|
|
Why string `json:"why"`
|
|
}
|
|
|
|
type claim struct {
|
|
Name string `json:"name"`
|
|
Scope string `json:"scope"`
|
|
Serves []string `json:"serves"`
|
|
}
|
|
|
|
type environment struct {
|
|
Variables map[string]string `json:"variables"`
|
|
Path []map[string]any `json:"path"`
|
|
}
|
|
|
|
type shellCode struct {
|
|
For string `json:"for"`
|
|
Slot string `json:"slot"`
|
|
Code string `json:"code"`
|
|
}
|
|
|
|
func readManifest(t *testing.T) manifest {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dec := json.NewDecoder(strings.NewReader(string(raw)))
|
|
dec.DisallowUnknownFields()
|
|
var m manifest
|
|
if err := dec.Decode(&m); err != nil {
|
|
t.Fatalf("module.json: %v", err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func (m manifest) resource(t *testing.T, id string) map[string]any {
|
|
t.Helper()
|
|
for _, r := range m.Resources {
|
|
if r["id"] == id {
|
|
return r
|
|
}
|
|
}
|
|
t.Fatalf("no resource %q", id)
|
|
return nil
|
|
}
|
|
|
|
func (m manifest) packages() (present, absent []string) {
|
|
for _, r := range m.Resources {
|
|
if r["type"] == "package" {
|
|
if r["absent"] == true {
|
|
absent = append(absent, r["package"].(string))
|
|
} else {
|
|
present = append(present, r["package"].(string))
|
|
}
|
|
}
|
|
}
|
|
return present, absent
|
|
}
|
|
|
|
// sameAsSource checks that a file resource's content is byte for byte the module's source file, so
|
|
// the readable file in the repository is what the machine gets.
|
|
func (m manifest) sameAsSource(t *testing.T, id, source string) {
|
|
t.Helper()
|
|
want, err := os.ReadFile(filepath.Join("..", "..", source))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := m.resource(t, id)
|
|
if r["type"] != "file" {
|
|
t.Fatalf("%s is a %v, not a file", id, r["type"])
|
|
}
|
|
if got, _ := r["content"].(string); got != string(want) {
|
|
t.Fatalf("resource %s's content is not %s: edit the source and copy it into module.json", id, source)
|
|
}
|
|
if r["owner"] != "${machine:account}" && !strings.HasPrefix(r["path"].(string), "/etc/") {
|
|
t.Fatalf("%s under the home is the account's", id)
|
|
}
|
|
}
|
|
|
|
// checkTheToolsAgree checks that the manifest lists the module's own tools exactly, that the bundle
|
|
// serves each seat verb the claims promise as <seat>.<verb>, and that the Go bundle is declared.
|
|
func checkTheToolsAgree(t *testing.T, m manifest) {
|
|
t.Helper()
|
|
own, seat := map[string]bool{}, map[string]bool{}
|
|
for _, tool := range tools() {
|
|
if strings.Contains(tool.Name, ".") {
|
|
seat[tool.Name] = true
|
|
} else {
|
|
own[tool.Name] = true
|
|
}
|
|
if strings.TrimSpace(tool.Description) == "" {
|
|
t.Errorf("%s has no description", tool.Name)
|
|
}
|
|
}
|
|
listed := map[string]bool{}
|
|
for _, name := range m.Tools {
|
|
listed[name] = true
|
|
if !own[name] {
|
|
t.Errorf("module.json lists %s, which the bundle does not serve", name)
|
|
}
|
|
}
|
|
for name := range own {
|
|
if !listed[name] {
|
|
t.Errorf("the bundle serves %s, which module.json does not list", name)
|
|
}
|
|
if !strings.HasPrefix(name, strings.ReplaceAll(m.Module, "-", "_")+"_") {
|
|
t.Errorf("%s is not prefixed with the module's name", name)
|
|
}
|
|
}
|
|
promised := map[string]bool{}
|
|
for _, c := range m.Claims {
|
|
for _, verb := range c.Serves {
|
|
promised[c.Name+"."+verb] = true
|
|
if !seat[c.Name+"."+verb] {
|
|
t.Errorf("the claim on %s promises %s, which the bundle does not serve", c.Name, verb)
|
|
}
|
|
}
|
|
}
|
|
for name := range seat {
|
|
if !promised[name] {
|
|
t.Errorf("the bundle serves %s, which no claim promises", name)
|
|
}
|
|
}
|
|
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/"+m.Module+"-tools" || bundle["binary"] != m.Module+"-tools" {
|
|
t.Errorf("the Go tools bundle: %v", bundle)
|
|
}
|
|
}
|
|
|
|
// checkNoSecretsOrInstallationNames refuses what a catalogue manifest must never carry.
|
|
func checkNoSecretsOrInstallationNames(t *testing.T) {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(filepath.Join("..", "..", "module.json"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := strings.ToLower(string(raw))
|
|
for _, never := range []string{"/home/", "jochen", "g14", "shanks", "novox.be", "api_key", ".hal/", "greenclip daemon"} {
|
|
if strings.Contains(s, never) {
|
|
t.Errorf("module.json names %q", never)
|
|
}
|
|
}
|
|
}
|