// Copyright 2026 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package specgen import ( "simd/archsimd/_gen/specgen/specexpr" "strings" "testing" ) func TestNewSpecTemplate(t *testing.T) { tests := []struct { tmpl string isTmpl bool wantErr bool }{ { tmpl: "", isTmpl: false, }, { tmpl: "Convert", isTmpl: false, }, { tmpl: "Convert{{.zL}}To{{.zB}}{{.zN}}", isTmpl: true, }, { tmpl: "Convert{{.zL", wantErr: true, }, { tmpl: "Convert{{if}}", wantErr: true, }, } for _, tc := range tests { got, err := newSpecTemplate(tc.tmpl) if (err != nil) != tc.wantErr { t.Errorf("newSpecTemplate(%q) returned error: %v, wantErr: %v", tc.tmpl, err, tc.wantErr) continue } if tc.wantErr { continue } if got.raw != tc.tmpl { t.Errorf("newSpecTemplate(%q) raw = %q, want %q", tc.tmpl, got.raw, tc.tmpl) } if (got.tmpl != nil) != tc.isTmpl { t.Errorf("newSpecTemplate(%q) isTmpl = %v, want %v", tc.tmpl, got.tmpl != nil, tc.isTmpl) } } } func TestSpecTemplateExpand(t *testing.T) { tmpl, err := newSpecTemplate("Convert{{.zL}}To{{.zB}}{{.zN}}") if err != nil { t.Fatalf("unexpected error parsing template: %v", err) } data := map[string]any{ "zL": specexpr.Int(4), "zB": "Float", "zN": specexpr.Int(32), } got, err := tmpl.expand(data) if err != nil { t.Fatalf("unexpected error expanding template: %v", err) } want := "Convert4ToFloat32" if got != want { t.Errorf("expected expanded string %q, got %q", want, got) } } func TestSpecTemplateTitle(t *testing.T) { tmpl, err := newSpecTemplate("ConvertTo{{.zE | title}}") if err != nil { t.Fatalf("unexpected error parsing template: %v", err) } data := map[string]any{ "zE": specexpr.Basic{Base: "float", Bits: 32}, } got, err := tmpl.expand(data) if err != nil { t.Fatalf("unexpected error expanding template: %v", err) } want := "ConvertToFloat32" if got != want { t.Errorf("expected %q, got %q", want, got) } } func TestSpecTemplateConditionals(t *testing.T) { tmplStr := `Doc for {{.Name}}. {{if lt .zL .xL}} Upper elements of the result are zeroed. {{end}}` tmpl, err := newSpecTemplate(tmplStr) if err != nil { t.Fatalf("unexpected error parsing template: %v", err) } // Case 1: zL < xL data1 := map[string]any{ "Name": "ConvertLo4ToFloat32", "zL": specexpr.Int(4), "xL": specexpr.Int(8), } got1, err := tmpl.expand(data1) if err != nil { t.Fatalf("unexpected error: %v", err) } if !strings.Contains(got1, "Upper elements of the result are zeroed.") { t.Errorf("expected conditional text when zL < xL, got: %q", got1) } // Case 2: zL >= xL data2 := map[string]any{ "Name": "ConvertLo8ToFloat32", "zL": specexpr.Int(8), "xL": specexpr.Int(8), } got2, err := tmpl.expand(data2) if err != nil { t.Fatalf("unexpected error: %v", err) } if strings.Contains(got2, "Upper elements of the result are zeroed.") { t.Errorf("expected no conditional text when zL == xL, got: %q", got2) } } func TestSpecTemplateScalableWidthCompare(t *testing.T) { tmpl, err := newSpecTemplate("{{if lt .zL .xL}}smaller{{else}}same-or-larger{{end}}") if err != nil { t.Fatalf("unexpected error: %v", err) } // zL = VW/32, xL = VW/16 => zL < xL zL, err := specexpr.VW().Div(specexpr.Int(32)) if err != nil { t.Fatal(err) } xL, err := specexpr.VW().Div(specexpr.Int(16)) if err != nil { t.Fatal(err) } data := map[string]any{ "zL": zL, "xL": xL, } got, err := tmpl.expand(data) if err != nil { t.Fatalf("unexpected error: %v", err) } if got != "smaller" { t.Errorf("expected 'smaller', got %q", got) } } func TestSpecTemplateMissingKey(t *testing.T) { tmpl, err := newSpecTemplate("Convert{{.missing}}") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = tmpl.expand(map[string]any{}) if err == nil { t.Errorf("expected error on missing key, got nil") } } func TestCleanDocNewlines(t *testing.T) { tests := []struct { input string want string }{ { input: "hello\n\n\n\nworld\n", want: "hello\n\nworld\n", }, { input: "hello\n\n\n", want: "hello\n", }, { input: "", want: "", }, } for _, tc := range tests { got := cleanDocNewlines(tc.input) if got != tc.want { t.Errorf("cleanDocNewlines(%q) = %q, want %q", tc.input, got, tc.want) } } } func TestReshapeDiagram(t *testing.T) { vec := func(n, l int) specexpr.Vector { return specexpr.Vector{Elem: specexpr.Basic{Base: "uint", Bits: specexpr.Int(n)}, Width: specexpr.Int(n * l)} } u8s := specexpr.Vector{Elem: specexpr.Basic{Base: "uint", Bits: 8}, Width: specexpr.VW()} u16s := specexpr.Vector{Elem: specexpr.Basic{Base: "uint", Bits: 16}, Width: specexpr.VW()} tests := []struct { name string x, z specexpr.Vector want string }{ { // Basic, no elision case name: "Uint32x4 -> Uint64x2", x: vec(32, 4), z: vec(64, 2), want: "\t x[3] x[2] x[1] x[0]\n" + "\t | 31 .. 0 | 31 .. 0 | 31 .. 0 | 31 .. 0 |\n" + "\t | 63 .... 0 | 63 .... 0 |\n" + "\t z[1] z[0]", }, { // Small to big, with elided middle name: "Uint32x8 -> Uint64x4", x: vec(32, 8), z: vec(64, 4), want: "\t x[7] x[6] ⋯ x[1] x[0]\n" + "\t | 31 .. 0 | 31 .. 0 | ⋯ | 31 .. 0 | 31 .. 0 |\n" + "\t | 63 .... 0 | ⋯ | 63 .... 0 |\n" + "\t z[3] ⋯ z[0]", }, { // Big to small, with elided middle name: "Uint64x4 -> Uint32x8", x: vec(64, 4), z: vec(32, 8), want: "\t x[3] ⋯ x[0]\n" + "\t | 63 .... 0 | ⋯ | 63 .... 0 |\n" + "\t | 31 .. 0 | 31 .. 0 | ⋯ | 31 .. 0 | 31 .. 0 |\n" + "\t z[7] z[6] ⋯ z[1] z[0]", }, { // Eliding elements within each big element name: "Uint64x2 -> Uint8x16", x: vec(64, 2), z: vec(8, 16), want: "\t x[1] x[0]\n" + "\t | 63 .... 0 | 63 .... 0 |\n" + "\t | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 |\n" + "\t z[15] z[14] ⋯ z[9] z[8] z[7] z[6] ⋯ z[1] z[0]", }, { // Eliding elements within each big element, and elided middle name: "Uint8x64 -> Uint64x8", x: vec(8, 64), z: vec(64, 8), want: "\t x[63] x[62] ⋯ x[57] x[56] ⋯ x[7] x[6] ⋯ x[1] x[0]\n" + "\t | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 | ⋯ | 7 .. 0 | 7 .. 0 |\n" + "\t | 63 .... 0 | ⋯ | 63 .... 0 |\n" + "\t z[7] ⋯ z[0]", }, { // Scalable name: "Uint8s -> Uint16s (Scalable)", x: u8s, z: u16s, want: "\t ⋯ x[3] x[2] x[1] x[0]\n" + "\t ⋯ | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |\n" + "\t ⋯ | 15 .... 0 | 15 .... 0 |\n" + "\t ⋯ z[1] z[0]", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := reshapeDiagram(tc.x, tc.z) if got != tc.want { t.Errorf("reshapeDiagram(%s, %s):\ngot:\n%s\nwant:\n%s", tc.x, tc.z, got, tc.want) } }) } }