| | |
| | |
| | |
| |
|
| | package test2json |
| |
|
| | import ( |
| | "bytes" |
| | "encoding/json" |
| | "flag" |
| | "fmt" |
| | "io" |
| | "os" |
| | "path/filepath" |
| | "reflect" |
| | "strings" |
| | "testing" |
| | "unicode/utf8" |
| | ) |
| |
|
| | var update = flag.Bool("update", false, "rewrite testdata/*.json files") |
| |
|
| | func TestGolden(t *testing.T) { |
| | files, err := filepath.Glob("testdata/*.test") |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | for _, file := range files { |
| | name := strings.TrimSuffix(filepath.Base(file), ".test") |
| | t.Run(name, func(t *testing.T) { |
| | orig, err := os.ReadFile(file) |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | |
| | |
| | var buf bytes.Buffer |
| | c := NewConverter(&buf, "", 0) |
| | in := append([]byte{}, orig...) |
| | for _, line := range bytes.SplitAfter(in, []byte("\n")) { |
| | writeAndKill(c, line) |
| | } |
| | c.Close() |
| |
|
| | if *update { |
| | js := strings.TrimSuffix(file, ".test") + ".json" |
| | t.Logf("rewriting %s", js) |
| | if err := os.WriteFile(js, buf.Bytes(), 0666); err != nil { |
| | t.Fatal(err) |
| | } |
| | return |
| | } |
| |
|
| | want, err := os.ReadFile(strings.TrimSuffix(file, ".test") + ".json") |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | diffJSON(t, buf.Bytes(), want) |
| | if t.Failed() { |
| | |
| | return |
| | } |
| |
|
| | |
| | t.Run("bulk", func(t *testing.T) { |
| | buf.Reset() |
| | c = NewConverter(&buf, "", 0) |
| | in = append([]byte{}, orig...) |
| | writeAndKill(c, in) |
| | c.Close() |
| | diffJSON(t, buf.Bytes(), want) |
| | }) |
| |
|
| | |
| | t.Run("crlf", func(t *testing.T) { |
| | buf.Reset() |
| | c = NewConverter(&buf, "", 0) |
| | in = bytes.ReplaceAll(orig, []byte("\n"), []byte("\r\n")) |
| | writeAndKill(c, in) |
| | c.Close() |
| | diffJSON(t, bytes.ReplaceAll(buf.Bytes(), []byte(`\r\n`), []byte(`\n`)), want) |
| | }) |
| |
|
| | |
| | t.Run("even2", func(t *testing.T) { |
| | buf.Reset() |
| | c = NewConverter(&buf, "", 0) |
| | in = append([]byte{}, orig...) |
| | for i := 0; i < len(in); i += 2 { |
| | if i+2 <= len(in) { |
| | writeAndKill(c, in[i:i+2]) |
| | } else { |
| | writeAndKill(c, in[i:]) |
| | } |
| | } |
| | c.Close() |
| | diffJSON(t, buf.Bytes(), want) |
| | }) |
| |
|
| | |
| | t.Run("odd2", func(t *testing.T) { |
| | buf.Reset() |
| | c = NewConverter(&buf, "", 0) |
| | in = append([]byte{}, orig...) |
| | if len(in) > 0 { |
| | writeAndKill(c, in[:1]) |
| | } |
| | for i := 1; i < len(in); i += 2 { |
| | if i+2 <= len(in) { |
| | writeAndKill(c, in[i:i+2]) |
| | } else { |
| | writeAndKill(c, in[i:]) |
| | } |
| | } |
| | c.Close() |
| | diffJSON(t, buf.Bytes(), want) |
| | }) |
| |
|
| | |
| | |
| | for b := 5; b <= 8; b++ { |
| | t.Run(fmt.Sprintf("tiny%d", b), func(t *testing.T) { |
| | oldIn := inBuffer |
| | oldOut := outBuffer |
| | defer func() { |
| | inBuffer = oldIn |
| | outBuffer = oldOut |
| | }() |
| | inBuffer = 64 |
| | outBuffer = b |
| | buf.Reset() |
| | c = NewConverter(&buf, "", 0) |
| | in = append([]byte{}, orig...) |
| | writeAndKill(c, in) |
| | c.Close() |
| | diffJSON(t, buf.Bytes(), want) |
| | }) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | func writeAndKill(w io.Writer, b []byte) { |
| | w.Write(b) |
| | for i := range b { |
| | b[i] = 'Z' |
| | } |
| | } |
| |
|
| | |
| | |
| | func diffJSON(t *testing.T, have, want []byte) { |
| | t.Helper() |
| | type event map[string]any |
| |
|
| | |
| | parseEvents := func(b []byte) ([]event, []string) { |
| | t.Helper() |
| | var events []event |
| | var lines []string |
| | for _, line := range bytes.SplitAfter(b, []byte("\n")) { |
| | if len(line) > 0 { |
| | line = bytes.TrimSpace(line) |
| | var e event |
| | err := json.Unmarshal(line, &e) |
| | if err != nil { |
| | t.Errorf("unmarshal %s: %v", b, err) |
| | continue |
| | } |
| | events = append(events, e) |
| | lines = append(lines, string(line)) |
| | } |
| | } |
| | return events, lines |
| | } |
| | haveEvents, haveLines := parseEvents(have) |
| | wantEvents, wantLines := parseEvents(want) |
| | if t.Failed() { |
| | return |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | i := 0 |
| | j := 0 |
| |
|
| | |
| | |
| | |
| | fail := func() { |
| | var buf bytes.Buffer |
| | show := func(i int, lines []string) { |
| | for k := -2; k < 5; k++ { |
| | marker := "" |
| | if k == 0 { |
| | marker = "» " |
| | } |
| | if 0 <= i+k && i+k < len(lines) { |
| | fmt.Fprintf(&buf, "\t%s%s\n", marker, lines[i+k]) |
| | } |
| | } |
| | if i >= len(lines) { |
| | |
| | fmt.Fprintf(&buf, "\t» \n") |
| | } |
| | } |
| | fmt.Fprintf(&buf, "have:\n") |
| | show(i, haveLines) |
| | fmt.Fprintf(&buf, "want:\n") |
| | show(j, wantLines) |
| | t.Fatal(buf.String()) |
| | } |
| |
|
| | var outputTest string |
| | var wantOutput, haveOutput string |
| |
|
| | |
| | getTest := func(e event) string { |
| | s, _ := e["Test"].(string) |
| | return s |
| | } |
| |
|
| | |
| | |
| | checkOutput := func() { |
| | for i < len(haveEvents) && haveEvents[i]["Action"] == "output" && getTest(haveEvents[i]) == outputTest { |
| | haveOutput += haveEvents[i]["Output"].(string) |
| | i++ |
| | } |
| | if haveOutput != wantOutput { |
| | t.Errorf("output mismatch for Test=%q:\nhave %q\nwant %q", outputTest, haveOutput, wantOutput) |
| | fail() |
| | } |
| | haveOutput = "" |
| | wantOutput = "" |
| | } |
| |
|
| | |
| | for j = range wantEvents { |
| | e := wantEvents[j] |
| | if e["Action"] == "output" && getTest(e) == outputTest { |
| | wantOutput += e["Output"].(string) |
| | continue |
| | } |
| | checkOutput() |
| | if e["Action"] == "output" { |
| | outputTest = getTest(e) |
| | wantOutput += e["Output"].(string) |
| | continue |
| | } |
| | if i >= len(haveEvents) { |
| | t.Errorf("early end of event stream: missing event") |
| | fail() |
| | } |
| | if !reflect.DeepEqual(haveEvents[i], e) { |
| | t.Errorf("events out of sync") |
| | fail() |
| | } |
| | i++ |
| | } |
| | checkOutput() |
| | if i < len(haveEvents) { |
| | t.Errorf("extra events in stream") |
| | fail() |
| | } |
| | } |
| |
|
| | func TestTrimUTF8(t *testing.T) { |
| | s := "hello α ☺ 😂 world" |
| | b := []byte(s) |
| | for i := 0; i < len(s); i++ { |
| | j := trimUTF8(b[:i]) |
| | u := string([]rune(s[:j])) + string([]rune(s[j:])) |
| | if u != s { |
| | t.Errorf("trimUTF8(%q) = %d (-%d), not at boundary (split: %q %q)", s[:i], j, i-j, s[:j], s[j:]) |
| | } |
| | if utf8.FullRune(b[j:i]) { |
| | t.Errorf("trimUTF8(%q) = %d (-%d), too early (missed: %q)", s[:j], j, i-j, s[j:i]) |
| | } |
| | } |
| | } |
| |
|