package catalogue import ( "encoding/json" "fmt" "strconv" "strings" "testing" ) // Defends novox/hq ADR 0255: a seat may receive a kind as data in its own shape, which the holder // renders with its template; the holder places every combination of the shape's placing fields once; // a contribution may be placed only where the machine reports a capability; and an offered kind makes // no dependency on its seat. const barTemplate = `{{if eq .shows "battery"}}[[block]] block = "battery" {{with .options}}{{with .device}}device = {{quote .}} {{end}}{{end}}{{else if eq .shows "command"}}[[block]] block = "custom" command = {{quote .options.command}} interval = {{.options.interval}} {{else if eq .shows "state"}}[[block]] block = "custom" command = {{quote (printf "show %s %s" .options.state (or .options.key .machine))}} {{end}}` func barHolder() Manifest { place := func(bar string) string { return "own-first\n${contribution:node-bar:block:bar=" + bar + ",place=resources}" + "own-middle\n${contribution:node-bar:block:bar=" + bar + ",place=status}own-last\n" } return Manifest{Module: "a-bar", Claims: []Claim{{Name: BarSeat, Renders: map[string]string{BarKindBlock: barTemplate}}}, Resources: []map[string]any{ {"id": "bottom", "type": "file", "path": "/b", "content": place("bottom")}, {"id": "top", "type": "file", "path": "/t", "content": place("top")}, }} } func block(data string) SeatContribution { var d map[string]any if err := json.Unmarshal([]byte(data), &d); err != nil { panic(err) } return SeatContribution{Seat: BarSeat, Kind: BarKindBlock, Data: d} } // tomlBasicString reads one TOML basic string, as the TOML specification (1.0, "String") defines it: // any character but a quote, a backslash and the control characters U+0000–U+0008, U+000A–U+001F // and U+007F, which are written as escapes. func tomlBasicString(s string) (string, error) { if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { return "", fmt.Errorf("not quoted") } var b strings.Builder rs := []rune(s[1 : len(s)-1]) for i := 0; i < len(rs); i++ { r := rs[i] switch { case r == '"': return "", fmt.Errorf("a bare quote at %d", i) case r == 0x7f || (r < 0x20 && r != '\t'): return "", fmt.Errorf("a bare control character %U at %d", r, i) case r != '\\': b.WriteRune(r) continue } i++ if i >= len(rs) { return "", fmt.Errorf("an escape at the end") } switch rs[i] { case 'b': b.WriteRune('\b') case 't': b.WriteRune('\t') case 'n': b.WriteRune('\n') case 'f': b.WriteRune('\f') case 'r': b.WriteRune('\r') case '"': b.WriteRune('"') case '\\': b.WriteRune('\\') case 'u', 'U': n := 4 if rs[i] == 'U' { n = 8 } if i+n > len(rs)-1 { return "", fmt.Errorf("a short \\u escape") } v, err := strconv.ParseUint(string(rs[i+1:i+1+n]), 16, 32) if err != nil { return "", err } b.WriteRune(rune(v)) i += n default: return "", fmt.Errorf("the escape \\%c is not TOML's", rs[i]) } } return b.String(), nil } func TestTheBarReceivesBlocksAsData(t *testing.T) { s, _ := SeatNamed(BarSeat) if len(s.Receives) != 1 || s.Receives[0].Kind != BarKindBlock || s.Receives[0].Shape == nil || !s.Receives[0].Offered { t.Fatalf("node-bar receives %+v", s.Receives) } for _, ex := range s.Receives[0].Shape.Examples { if p := barBlockProblems(ex); len(p) > 0 { t.Errorf("the shape's own example %v is refused: %v", ex, p) } } } func TestPerMachineStateIsDeclaredAndKeptInItsLongForm(t *testing.T) { m, err := decodeForTest([]byte(`{"module":"power","state":[{"name":"draw","ttl-seconds":30,"per-machine":true}]}`)) if err != nil || len(m.State) != 1 || !m.State[0].PerMachine { t.Fatalf("%+v %v", m.State, err) } raw, _ := json.Marshal(StateDeclaration{Name: "draw", PerMachine: true}) if string(raw) != `{"name":"draw","per-machine":true}` { t.Fatalf("%s", raw) } }