312 lines
9.0 KiB
Go
312 lines
9.0 KiB
Go
package helpers
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func captureDocFragmentStderr(t *testing.T, run func()) string {
|
|
t.Helper()
|
|
reader, writer, err := os.Pipe()
|
|
if err != nil {
|
|
t.Fatalf("create stderr pipe: %v", err)
|
|
}
|
|
previous := os.Stderr
|
|
os.Stderr = writer
|
|
defer func() {
|
|
os.Stderr = previous
|
|
_ = writer.Close()
|
|
_ = reader.Close()
|
|
}()
|
|
|
|
run()
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatalf("close stderr writer: %v", err)
|
|
}
|
|
os.Stderr = previous
|
|
output, err := io.ReadAll(reader)
|
|
if err != nil {
|
|
t.Fatalf("read stderr: %v", err)
|
|
}
|
|
return string(output)
|
|
}
|
|
|
|
func TestCrossPlatformCoverageJSONMLFragmentStripHelpers(t *testing.T) {
|
|
t.Run("child start", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
node []any
|
|
want int
|
|
}{
|
|
{name: "empty", node: nil, want: 1},
|
|
{name: "tag only", node: []any{"p"}, want: 1},
|
|
{name: "first child", node: []any{"p", "text"}, want: 1},
|
|
{name: "attributes", node: []any{"p", map[string]any{}}, want: 2},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := jsonmlChildStart(tt.node); got != tt.want {
|
|
t.Fatalf("jsonmlChildStart(%#v) = %d, want %d", tt.node, got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("children recursively splice fragments", func(t *testing.T) {
|
|
children := []any{
|
|
"text",
|
|
[]any{},
|
|
[]any{
|
|
"fragment",
|
|
map[string]any{"source": "range"},
|
|
[]any{"p", map[string]any{}},
|
|
[]any{"fragment", []any{"h1", map[string]any{}}},
|
|
},
|
|
[]any{
|
|
"div",
|
|
map[string]any{},
|
|
[]any{"fragment", map[string]any{}, []any{"span", map[string]any{}, "inside"}},
|
|
},
|
|
}
|
|
got, count := stripFragmentChildren(children)
|
|
want := []any{
|
|
"text",
|
|
[]any{},
|
|
[]any{"p", map[string]any{}},
|
|
[]any{"h1", map[string]any{}},
|
|
[]any{"div", map[string]any{}, []any{"span", map[string]any{}, "inside"}},
|
|
}
|
|
if count != 3 || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("stripFragmentChildren() = %#v, %d; want %#v, 3", got, count, want)
|
|
}
|
|
})
|
|
|
|
t.Run("ordinary children are unchanged", func(t *testing.T) {
|
|
children := []any{"text", []any{"p", map[string]any{}, "body"}}
|
|
got, count := stripFragmentChildren(children)
|
|
if count != 0 || !reflect.DeepEqual(got, children) {
|
|
t.Fatalf("stripFragmentChildren() = %#v, %d", got, count)
|
|
}
|
|
})
|
|
|
|
t.Run("node branches", func(t *testing.T) {
|
|
leaf := []any{"p", map[string]any{}}
|
|
got, count := stripFragmentInNode(leaf)
|
|
if count != 0 || !reflect.DeepEqual(got, leaf) {
|
|
t.Fatalf("leaf = %#v, %d", got, count)
|
|
}
|
|
|
|
ordinary := []any{"p", map[string]any{}, []any{"span", map[string]any{}, "body"}}
|
|
got, count = stripFragmentInNode(ordinary)
|
|
if count != 0 || !reflect.DeepEqual(got, ordinary) {
|
|
t.Fatalf("ordinary = %#v, %d", got, count)
|
|
}
|
|
|
|
nested := []any{"p", map[string]any{}, []any{"fragment", map[string]any{}, []any{"span", map[string]any{}, "body"}}}
|
|
got, count = stripFragmentInNode(nested)
|
|
want := []any{"p", map[string]any{}, []any{"span", map[string]any{}, "body"}}
|
|
if count != 1 || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("nested = %#v, %d; want %#v, 1", got, count, want)
|
|
}
|
|
})
|
|
|
|
t.Run("body branches", func(t *testing.T) {
|
|
if got, count := stripBodyFragments(nil); got != nil || count != 0 {
|
|
t.Fatalf("empty body = %#v, %d", got, count)
|
|
}
|
|
|
|
ordinary := []any{"root", map[string]any{}, []any{"p", map[string]any{}}}
|
|
got, count := stripBodyFragments(ordinary)
|
|
if count != 0 || !reflect.DeepEqual(got, ordinary) {
|
|
t.Fatalf("ordinary body = %#v, %d", got, count)
|
|
}
|
|
|
|
top := []any{
|
|
"fragment",
|
|
map[string]any{"source": "outline"},
|
|
[]any{"fragment", map[string]any{}, []any{"h1", map[string]any{}}},
|
|
}
|
|
got, count = stripBodyFragments(top)
|
|
want := []any{"root", map[string]any{}, []any{"h1", map[string]any{}}}
|
|
if count != 2 || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("top fragment body = %#v, %d; want %#v, 2", got, count, want)
|
|
}
|
|
})
|
|
|
|
t.Run("single node branches", func(t *testing.T) {
|
|
if got, count, err := stripNodeFragments(nil); err != nil || got != nil || count != 0 {
|
|
t.Fatalf("empty node = %#v, %d, %v", got, count, err)
|
|
}
|
|
|
|
ordinary := []any{"p", map[string]any{}, "body"}
|
|
got, count, err := stripNodeFragments(ordinary)
|
|
if err != nil || count != 0 || !reflect.DeepEqual(got, ordinary) {
|
|
t.Fatalf("ordinary node = %#v, %d, %v", got, count, err)
|
|
}
|
|
|
|
nested := []any{"p", map[string]any{}, []any{"fragment", map[string]any{}, []any{"span", map[string]any{}, "body"}}}
|
|
got, count, err = stripNodeFragments(nested)
|
|
want := []any{"p", map[string]any{}, []any{"span", map[string]any{}, "body"}}
|
|
if err != nil || count != 1 || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("nested node = %#v, %d, %v; want %#v", got, count, err, want)
|
|
}
|
|
|
|
top := []any{
|
|
"fragment",
|
|
map[string]any{"source": "section"},
|
|
[]any{"fragment", map[string]any{}, []any{"p", map[string]any{}, "body"}},
|
|
}
|
|
got, count, err = stripNodeFragments(top)
|
|
want = []any{"p", map[string]any{}, "body"}
|
|
if err != nil || count != 2 || !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("top node = %#v, %d, %v; want %#v", got, count, err, want)
|
|
}
|
|
})
|
|
|
|
t.Run("single node top fragment errors", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
node []any
|
|
want string
|
|
}{
|
|
{
|
|
name: "empty",
|
|
node: []any{"fragment", map[string]any{}},
|
|
want: "无可写回的节点",
|
|
},
|
|
{
|
|
name: "non node child",
|
|
node: []any{"fragment", map[string]any{}, "text"},
|
|
want: "子节点不是合法 JSONML 节点",
|
|
},
|
|
{
|
|
name: "multiple nodes",
|
|
node: []any{"fragment", map[string]any{}, []any{"p", map[string]any{}}, []any{"p", map[string]any{}}},
|
|
want: "一次只能写一个",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, count, err := stripNodeFragments(tt.node)
|
|
if err == nil || !strings.Contains(err.Error(), tt.want) {
|
|
t.Fatalf("result = %#v, %d, %v; want error containing %q", got, count, err, tt.want)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestCrossPlatformCoveragePrepareJSONMLStripsReadOnlyFragments(t *testing.T) {
|
|
strict := jsonMLTestCommand(t, false)
|
|
|
|
t.Run("body top fragment becomes root", func(t *testing.T) {
|
|
var got string
|
|
var prepareErr error
|
|
stderr := captureDocFragmentStderr(t, func() {
|
|
got, prepareErr = prepareJsonMLBody(
|
|
strict,
|
|
`{"jsonml":["fragment",{"source":"outline"},["p",{},["span",{},"ok"]]]}`,
|
|
)
|
|
})
|
|
if prepareErr != nil {
|
|
t.Fatalf("prepare body: %v", prepareErr)
|
|
}
|
|
want := `["root",{},["p",{},["span",{},"ok"]]]`
|
|
if got != want {
|
|
t.Fatalf("body = %s, want %s", got, want)
|
|
}
|
|
if !strings.Contains(stderr, "fragment 只读容器") || !strings.Contains(stderr, "已自动移除 1 处") {
|
|
t.Fatalf("stderr = %q", stderr)
|
|
}
|
|
})
|
|
|
|
t.Run("body nested fragment is spliced", func(t *testing.T) {
|
|
var got string
|
|
var prepareErr error
|
|
stderr := captureDocFragmentStderr(t, func() {
|
|
got, prepareErr = prepareJsonMLBody(
|
|
strict,
|
|
`{"jsonml":["root",{},["fragment",{},["p",{},["span",{},"nested"]]]]}`,
|
|
)
|
|
})
|
|
if prepareErr != nil {
|
|
t.Fatalf("prepare body: %v", prepareErr)
|
|
}
|
|
want := `["root",{},["p",{},["span",{},"nested"]]]`
|
|
if got != want {
|
|
t.Fatalf("body = %s, want %s", got, want)
|
|
}
|
|
if !strings.Contains(stderr, "已自动移除 1 处") {
|
|
t.Fatalf("stderr = %q", stderr)
|
|
}
|
|
})
|
|
|
|
t.Run("node top fragment is unwrapped", func(t *testing.T) {
|
|
var got string
|
|
var prepareErr error
|
|
stderr := captureDocFragmentStderr(t, func() {
|
|
got, prepareErr = prepareJsonMLNode(
|
|
strict,
|
|
`["fragment",{"source":"section"},["p",{},["span",{},"ok"]]]`,
|
|
)
|
|
})
|
|
if prepareErr != nil {
|
|
t.Fatalf("prepare node: %v", prepareErr)
|
|
}
|
|
want := `["p",{},["span",{},"ok"]]`
|
|
if got != want {
|
|
t.Fatalf("node = %s, want %s", got, want)
|
|
}
|
|
if !strings.Contains(stderr, "fragment 只读容器") || !strings.Contains(stderr, "已自动移除 1 处") {
|
|
t.Fatalf("stderr = %q", stderr)
|
|
}
|
|
})
|
|
|
|
t.Run("node nested fragment is spliced", func(t *testing.T) {
|
|
var got string
|
|
var prepareErr error
|
|
stderr := captureDocFragmentStderr(t, func() {
|
|
got, prepareErr = prepareJsonMLNode(
|
|
strict,
|
|
`["p",{},["fragment",{},["span",{},"nested"]]]`,
|
|
)
|
|
})
|
|
if prepareErr != nil {
|
|
t.Fatalf("prepare node: %v", prepareErr)
|
|
}
|
|
want := `["p",{},["span",{},"nested"]]`
|
|
if got != want {
|
|
t.Fatalf("node = %s, want %s", got, want)
|
|
}
|
|
if !strings.Contains(stderr, "已自动移除 1 处") {
|
|
t.Fatalf("stderr = %q", stderr)
|
|
}
|
|
})
|
|
|
|
t.Run("top node fragment errors remain actionable", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
raw string
|
|
want string
|
|
}{
|
|
{name: "empty", raw: `["fragment",{}]`, want: "无可写回的节点"},
|
|
{name: "text", raw: `["fragment",{},"text"]`, want: "子节点不是合法 JSONML 节点"},
|
|
{
|
|
name: "multiple",
|
|
raw: `["fragment",{},["p",{}],["p",{}]]`,
|
|
want: "一次只能写一个",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if _, err := prepareJsonMLNode(strict, tt.raw); err == nil || !strings.Contains(err.Error(), tt.want) {
|
|
t.Fatalf("prepareJsonMLNode(%s) error = %v, want %q", tt.raw, err, tt.want)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|