Owns both bars and their icons, the bar blocks as an i3 drop-in; the battery, GPU and headset blocks leave (hardware and a person's devices), the weather needs no key; the update count and the bar watchdog become module code, the watchdog started once per session. Go tools reload, blocks, block-run and themes.
142 lines
5.0 KiB
Go
142 lines
5.0 KiB
Go
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)
|
|
}
|
|
}
|