this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

doc/ref: start testing CUE code blocks in the spec via `go test`

For now, this just enforces valid CUE syntax for the code blocks
annotated accordingly. We use https://github.com/yuin/goldmark,
which had been a dependency until recently as we had been using it
to detect GitHub usernames being pinged by commit messages.

We use a Go test as it will run as part of `go test ./...`,
and we can also leverage env vars like CUE_UPDATE=1 in the future.
We considered using a standalone main func invoked via `go generate`,
but that required a Go package file with a `//go:generate` directive
and we did not want to make doc/ref an importable Go package.

Signed-off-by: Daniel Martí <mvdan@mvdan.cc>
Change-Id: I1607b1edadb937937a7f04c67112d7c0bcda815d
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1232179
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+106
+103
doc/ref/spec_test.go
··· 1 + // Copyright 2026 The CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + package ref_test 16 + 17 + import ( 18 + "bytes" 19 + "fmt" 20 + "os" 21 + "strings" 22 + "testing" 23 + 24 + "cuelang.org/go/cue/parser" 25 + "github.com/yuin/goldmark" 26 + goldast "github.com/yuin/goldmark/ast" 27 + "github.com/yuin/goldmark/text" 28 + ) 29 + 30 + func TestSpecCheck(t *testing.T) { 31 + source, err := os.ReadFile("spec.md") 32 + if err != nil { 33 + t.Fatal(err) 34 + } 35 + 36 + md := goldmark.New() 37 + doc := md.Parser().Parse(text.NewReader(source)) 38 + 39 + walkNode(t, doc, source) 40 + } 41 + 42 + func walkNode(t *testing.T, node goldast.Node, source []byte) { 43 + if fcb, ok := node.(*goldast.FencedCodeBlock); ok { 44 + checkBlock(t, fcb, source) 45 + } 46 + for child := node.FirstChild(); child != nil; child = child.NextSibling() { 47 + walkNode(t, child, source) 48 + } 49 + } 50 + 51 + func checkBlock(t *testing.T, fcb *goldast.FencedCodeBlock, source []byte) { 52 + if fcb.Info == nil { 53 + return 54 + } 55 + info := string(fcb.Info.Value(source)) 56 + fields := strings.Fields(info) 57 + if len(fields) == 0 { 58 + return 59 + } 60 + 61 + // Compute the markdown line number for the opening ``` line. 62 + mdLine := 0 63 + if fcb.Lines().Len() > 0 { 64 + startOffset := fcb.Lines().At(0).Start 65 + mdLine = bytes.Count(source[:startOffset], []byte("\n")) 66 + } 67 + pos := fmt.Sprintf("spec.md:%d", mdLine) 68 + 69 + lang := fields[0] 70 + if lang != "cue" { 71 + return // skip non-CUE code blocks 72 + } 73 + 74 + // By default, we check for valid syntax. 75 + // TODO: mark intent (export, eval, vet) and validate it. 76 + mode := "parse" 77 + if len(fields) > 1 { 78 + mode = fields[1] 79 + } 80 + switch mode { 81 + case "parse": 82 + case "rows": 83 + // TODO: parse and validate line by line 84 + return 85 + case "untested": 86 + return // intentionally not tested 87 + default: 88 + t.Errorf("%s: unknown cue code block mode: ```%s", pos, info) 89 + return 90 + } 91 + 92 + var buf bytes.Buffer 93 + for i := 0; i < fcb.Lines().Len(); i++ { 94 + line := fcb.Lines().At(i) 95 + buf.Write(line.Value(source)) 96 + } 97 + src := buf.String() 98 + 99 + _, err := parser.ParseFile(pos, src, parser.ParseComments) 100 + if err != nil { 101 + t.Errorf("%s: %q block failed to parse:\n%s", pos, info, err) 102 + } 103 + }
+1
go.mod
··· 18 18 github.com/spf13/cobra v1.10.2 19 19 github.com/spf13/pflag v1.0.10 20 20 github.com/tetratelabs/wazero v1.11.0 21 + github.com/yuin/goldmark v1.7.16 21 22 go.yaml.in/yaml/v3 v3.0.4 22 23 golang.org/x/mod v0.33.0 23 24 golang.org/x/net v0.50.0
+2
go.sum
··· 46 46 github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 47 47 github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= 48 48 github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= 49 + github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE= 50 + github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= 49 51 go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 50 52 go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 51 53 golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=