Let the bar render blocks other modules contribute, and give it the battery and power draw from the power module (hq ADR 0255)
The bar left the battery out because it belongs to whoever knows the hardware, and had no way to take it back in. i3status-rust now renders bar-neutral blocks through its template; the power module contributes the battery where the machine reports one and the power draw where something measures it, sampling the root-only energy counter as root and publishing only a rate. The memory block shows what programs hold, the figure memory-pressure judges by, instead of counting the cache as used.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"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"}
|
||||
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(), "<no value>") {
|
||||
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")
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ type claim struct {
|
||||
Name string `json:"name"`
|
||||
Scope string `json:"scope"`
|
||||
Serves []string `json:"serves"`
|
||||
// How the holder renders each kind its seat receives as data (novox/hq ADR 0255).
|
||||
Renders map[string]string `json:"renders"`
|
||||
}
|
||||
|
||||
type environment struct {
|
||||
|
||||
@@ -16,7 +16,8 @@ func TestItClaimsTheBarSeatAndRequiresTheXDisplay(t *testing.T) {
|
||||
if m.Module != "i3status-rust" || m.Seats != nil {
|
||||
t.Fatalf("module %q declares seats %v", m.Module, m.Seats)
|
||||
}
|
||||
if !reflect.DeepEqual(m.Claims, []claim{{Name: "node-bar", Scope: "node"}}) {
|
||||
if len(m.Claims) != 1 || m.Claims[0].Name != "node-bar" || m.Claims[0].Scope != "node" ||
|
||||
len(m.Claims[0].Renders) != 1 || m.Claims[0].Renders["block"] == "" {
|
||||
t.Fatalf("claims: %+v", m.Claims)
|
||||
}
|
||||
if !reflect.DeepEqual(m.Requires, []string{"x11-display"}) {
|
||||
|
||||
Reference in New Issue
Block a user