package main import ( "os" "path/filepath" "reflect" "strings" "testing" ) func source(t *testing.T, name string) string { t.Helper() raw, err := os.ReadFile(filepath.Join("..", "..", "files", name)) if err != nil { t.Fatal(err) } return string(raw) } func TestTheModulesBarsSplitIntoTheirBlocksInOrder(t *testing.T) { c := splitConfig(source(t, "bottom-bar.toml")) var kinds []string for i, b := range c.Blocks { kinds = append(kinds, describe(i, b).Kind) } want := []string{"tea_timer", "custom", "cpu", "disk_space", "disk_space", "memory", "docker", "sound", "weather", "notify", "time"} if !reflect.DeepEqual(kinds, want) { t.Fatalf("%v", kinds) } if !strings.Contains(c.Header, `theme = "plain"`) || strings.Contains(c.Header, "[[block]]") { t.Fatalf("header: %s", c.Header) } custom := describe(1, c.Blocks[1]) if custom.Settings["command"] != "~/.local/bin/i3status-updates 10" || custom.Settings["interval"] != "1800" { t.Fatalf("a block's own settings, not its click's: %+v", custom) } if _, leaked := custom.Settings["cmd"]; leaked { t.Fatal("the click's cmd was read as the block's") } top := splitConfig(source(t, "top-bar.toml")) if len(top.Blocks) != 2 || describe(0, top.Blocks[0]).Kind != "focused_window" { t.Fatalf("top: %+v", top.Blocks) } } // Two updates of i3bar's protocol, as i3status-rs wrote them for the top bar on 2026-10-04. const stream = `{"version": 1, "click_events": true} [ [{"full_text":" | ","color":"#FFFFFFFF","separator":false},{"full_text":" up 1d ","color":"#F1F1F1FF","name":"0","instance":"1:"}], [{"full_text":" | ","color":"#FFFFFFFF","separator":false},{"full_text":" ","color":"#FF0000FF","name":"0","instance":"0:"},{"full_text":"failed to connect to wayland ","color":"#FF0000FF","name":"0","instance":"0:"},{"full_text":" | "},{"full_text":" up 2d ","color":"#F1F1F1FF","name":"0","instance":"1:"}], ` func TestTheLatestUpdateIsReadPerBlockIndex(t *testing.T) { got := parseStatus(stream) if got[1].text != "up 2d" || got[1].state != "" || got[0].text != "failed to connect to wayland" || got[0].state != "critical" || len(got) != 2 { t.Fatalf("%+v", got) } if len(parseStatus("garbage\n[\n")) != 0 { t.Fatal("no update is no blocks") } } // installBars puts the module's bars where i3status-rs would read them, and a fake i3status-rs. func installBars(t *testing.T) string { t.Helper() root := fakeMachine(t) dir := filepath.Join(root, "home", ".config", "i3status-rust") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } for _, f := range []string{"top-bar.toml", "bottom-bar.toml"} { if err := os.WriteFile(filepath.Join(dir, f), []byte(source(t, f)), 0o644); err != nil { t.Fatal(err) } } bin := fakeBinaries(t, map[string]string{"i3status-rs": `cp "$1" "$LOG.config" printf '%s\n' '{"version": 1}' '[' '[{"full_text":"first","instance":"0:"},{"full_text":"second","instance":"1:"}],'`}) t.Setenv("LOG", filepath.Join(bin, "log")) return bin } func TestBlocksPairEachBlockWithWhatItShows(t *testing.T) { installBars(t) got, err := Blocks("top", true) if err != nil || len(got) != 1 { t.Fatalf("%+v, %v", got, err) } b := got[0].Blocks if len(b) != 2 || b[0].Kind != "focused_window" || b[0].Text == nil || *b[0].Text != "first" || *b[1].Text != "second" { t.Fatalf("%+v", got) } if all, _ := Blocks("", false); len(all) != 2 || all[1].Blocks[0].Text != nil { t.Fatalf("both bars, without values: %+v", all) } if _, err := Blocks("side", false); err == nil { t.Fatal("an unknown bar was accepted") } } func TestOneBlockRunsAloneWithTheBarsThemeAndIcons(t *testing.T) { bin := installBars(t) got, err := BlockRun("bottom", 8) if err != nil || got.Block.Kind != "weather" || got.Block.Text == nil || *got.Block.Text != "first" { t.Fatalf("%+v, %v", got, err) } ran, _ := os.ReadFile(filepath.Join(bin, "log.config")) if strings.Count(string(ran), "[[block]]") != 1 || !strings.Contains(string(ran), `name = "metno"`) || !strings.Contains(string(ran), `icons = "custom-icons"`) { t.Fatalf("the config it ran:\n%s", ran) } if _, err := BlockRun("bottom", 11); err == nil { t.Fatal("a block past the end was accepted") } if _, err := BlockRun("", 0); err == nil { t.Fatal("no bar was accepted") } } func TestThemesListEachOnceWithTheOnesInUse(t *testing.T) { installBars(t) root := t.TempDir() for _, f := range []string{"mine/plain.toml", "system/plain.toml", "system/nord-dark.toml", "system/README"} { if err := os.MkdirAll(filepath.Dir(filepath.Join(root, f)), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(root, f), nil, 0o644); err != nil { t.Fatal(err) } } got := Themes([]themeDir{{filepath.Join(root, "mine"), "mine"}, {filepath.Join(root, "system"), "system"}}) if got.InUse["top"] != "plain" || got.InUse["bottom"] != "plain" || len(got.Themes) != 2 || got.Themes[1].Source != "mine" { t.Fatalf("%+v", got) } } func TestReloadWithNoBarRunningSaysSo(t *testing.T) { fakeMachine(t) if r := Reload(); len(r.Reached) != 0 || !strings.Contains(r.Note, "no bar") { t.Fatalf("%+v", r) } }