package main import ( "bytes" "encoding/json" "fmt" "os" "path/filepath" "strings" "testing" "text/template" ) // i3status-rust renders the blocks other modules contribute (novox/hq ADR 0255): a bar-neutral piece // — what it shows, on which bar, where — in its own grammar, through the template its claim gives. The // controller runs the template; these tests run it the same way, with the one function it is given. func blockTemplate(t *testing.T) *template.Template { t.Helper() m := readManifest(t) src, err := os.ReadFile("../../files/block.tmpl") if err != nil { t.Fatal(err) } if m.Claims[0].Renders["block"] != string(src) { t.Fatal("the claim's template is not files/block.tmpl") } quote := func(v any) (string, error) { b, err := json.Marshal(fmt.Sprint(v)) return string(b), err } return template.Must(template.New("block").Funcs(template.FuncMap{"quote": quote}).Parse(string(src))) } func render(t *testing.T, tmpl *template.Template, piece string) string { t.Helper() view := map[string]any{"module": "power", "machine": "laptop"} if err := json.Unmarshal([]byte(piece), &view); err != nil { t.Fatal(err) } var b bytes.Buffer if err := tmpl.Execute(&b, view); err != nil { t.Fatal(err) } if strings.Contains(b.String(), "") { t.Fatalf("%s rendered a missing value: %q", piece, b.String()) } return b.String() } func TestAContributedBatteryIsI3statusRustsBatteryBlock(t *testing.T) { tmpl := blockTemplate(t) got := render(t, tmpl, `{"bar":"bottom","place":"status","shows":"battery"}`) if !strings.HasPrefix(got, "[[block]]\nblock = \"battery\"\nformat = ") || strings.Contains(got, "device") || !strings.Contains(got, `missing_format = ""`) || !strings.HasSuffix(got, "\n\n") { t.Fatalf("%q", got) } got = render(t, tmpl, `{"bar":"bottom","place":"status","shows":"battery","options":{"device":"BAT1"}}`) if !strings.Contains(got, "block = \"battery\"\ndevice = \"BAT1\"\nformat = ") { t.Fatalf("%q", got) } } func TestAContributedCommandIsACustomBlockQuoted(t *testing.T) { got := render(t, blockTemplate(t), `{"bar":"bottom","place":"status","shows":"command","options":{"command":"say \"hi\"","interval":5}}`) want := "[[block]]\nblock = \"custom\"\ncommand = \"say \\\"hi\\\"\"\ninterval = 5\n\n" if got != want { t.Fatalf("%q, want %q", got, want) } } func TestEachBarPlacesBothPlacesOnce(t *testing.T) { m := readManifest(t) for _, bar := range []string{"bottom", "top"} { content := m.resource(t, bar+"-bar")["content"].(string) for _, place := range []string{"resources", "status"} { ph := "${contribution:node-bar:block:bar=" + bar + ",place=" + place + "}" if strings.Count(content, ph) != 1 { t.Errorf("the %s bar places %s %d times", bar, ph, strings.Count(content, ph)) } } } bottom := m.resource(t, "bottom-bar")["content"].(string) resources := strings.Index(bottom, "place=resources") if !(strings.Index(bottom, `block = "memory"`) < resources && resources < strings.Index(bottom, `block = "docker"`)) || !(strings.Index(bottom, `block = "sound"`) < strings.Index(bottom, "place=status") && strings.Index(bottom, "place=status") < strings.Index(bottom, `block = "weather"`)) { t.Fatal("the places are not beside the memory and beside the sound") } } func TestTheMemoryIsWhatProgramsHoldNotTheCache(t *testing.T) { bottom := readManifest(t).resource(t, "bottom-bar")["content"].(string) if !strings.Contains(bottom, `format = "$icon $mem_used.eng(w:2) ($mem_used_percents.eng(w:2))"`) || strings.Contains(bottom, "mem_total_used") { t.Fatal("the memory block counts the cache as used") } } func TestAContributedStateIsACustomBlockReadingThisMachinesValue(t *testing.T) { tmpl := blockTemplate(t) got := render(t, tmpl, `{"bar":"bottom","place":"status","shows":"state","options":{"state":"power.draw"}}`) want := "[[block]]\nblock = \"custom\"\ncommand = \"~/.local/bin/i3status-value power.draw laptop\"\ninterval = 5\nhide_when_empty = true\n\n" if got != want { t.Fatalf("%q, want %q", got, want) } got = render(t, tmpl, `{"bar":"bottom","place":"status","shows":"state","options":{"state":"power.draw","key":"desk"}}`) if !strings.Contains(got, "i3status-value power.draw desk") { t.Fatalf("%q", got) } // And the bundle finds what it rendered. f := filepath.Join(t.TempDir(), "bottom-bar.toml") if err := os.WriteFile(f, []byte(got), 0o644); err != nil { t.Fatal(err) } if w := wantedIn([]string{f}); len(w) != 1 || w[0] != (Wanted{"power.draw", "desk"}) { t.Fatalf("%v", w) } }