diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 571298e..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Release - -on: - push: - tags: ['v*'] - -permissions: - contents: write - -jobs: - release: - name: Gitea release from CHANGELOG.md - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - - run: python3 release-tooling/publish_release.py - env: - GITEA_API_URL: ${{ github.api_url }} - GITEA_REPOSITORY: ${{ github.repository }} - GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG: ${{ github.ref_name }} diff --git a/shape_test.go b/shape_test.go index acc1d33..04516d8 100644 --- a/shape_test.go +++ b/shape_test.go @@ -2,6 +2,7 @@ package fejkdata import ( "os" + "sort" "strconv" "strings" "testing" @@ -10,8 +11,8 @@ import ( const shapePin = "testdata/shipped_shape.txt" // TestShippedShapeIsPinned pins what a version promises about the shipped data (see -// the README's Versioning): every path, each category's format, and each record -// column's datatype and nullability. REPIN=1 rewrites the pin. +// the README's Versioning): every path, each category's format and the categories it +// reads, and each record column's datatype and nullability. REPIN=1 rewrites the pin. func TestShippedShapeIsPinned(t *testing.T) { f, err := New(WithSeed(1)) if err != nil { @@ -33,8 +34,9 @@ func TestShippedShapeIsPinned(t *testing.T) { } } -// shippedShape lists every path Fake accepts, one per line. A category-level template -// carries its format, and each of its columns its datatype and whether it may be null. +// shippedShape lists every path Fake accepts, one per line. A category carries the +// categories it references, a category-level template its format, and each of its +// columns its datatype and whether it may be null. func shippedShape(f *Generator) string { facts := map[string]string{} var walk func(prefix string, n node) @@ -44,8 +46,10 @@ func shippedShape(f *Generator) string { for _, name := range sortedNames(n.children) { walk(join(prefix, name), n.children[name]) } + case *choice: + facts[prefix] = reads(n) case *template: - facts[prefix] = "\tformat " + strconv.Quote(n.format) + facts[prefix] = "\tformat " + strconv.Quote(n.format) + reads(n) if _, columns, err := recordOf(n); err == nil { for _, c := range columns { fact := "\t" + c.DataType.String() @@ -66,3 +70,49 @@ func shippedShape(f *Generator) string { } return b.String() } + +// reads names the categories any template under n references, sorted. +func reads(n node) string { + set := map[string]bool{} + var collect func(node) + collect = func(n node) { + switch n := n.(type) { + case *choice: + for _, it := range n.items { + collect(it) + } + case *template: + for _, b := range n.refs { + set[strings.TrimPrefix(b.key, "/")] = true + } + for name, field := range n.fields { + if !isRef(name) { + collect(field) + } + } + } + } + collect(n) + if len(set) == 0 { + return "" + } + keys := make([]string, 0, len(set)) + for k := range set { + keys = append(keys, k) + } + sort.Strings(keys) + return "\treads " + strings.Join(keys, " ") +} + +func TestShippedShapeNamesReads(t *testing.T) { + f := newGenerator(t, writeData(t, map[string]string{ + "a": `{"format":"{x}","x":["{/b}",{"format":"{/c.v}","weight":2}]}`, + "b": `"y"`, + "c": `{"format":"{v}","v":["z","w"]}`, + "d/pos": `["{/b}","q"]`, + })) + want := "a\tformat \"{x}\"\treads b c\na.x\tstring\nb\tformat \"y\"\nc\tformat \"{v}\"\nc.v\tstring\nd.pos\treads b\n" + if got := shippedShape(f); got != want { + t.Fatalf("shippedShape =\n%s\nwant\n%s", got, want) + } +}