From 0b243859fc9b184ec66228fd68c621ae4e3c6933 Mon Sep 17 00:00:00 2001 From: karitham Date: Sat, 22 Aug 2026 00:14:17 +0200 Subject: [PATCH] formatter: roundtrip the full tests/ corpus The evernote, galaxy, and line-protocol fixtures were checked in but referenced by no test. Feed every .thrift under tests/ through format -> reparse -> reformat and assert zero errors and idempotency per file. --- formatter/corpus_test.go | 89 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 formatter/corpus_test.go diff --git a/formatter/corpus_test.go b/formatter/corpus_test.go new file mode 100644 index 0000000..7fd6398 --- /dev/null +++ b/formatter/corpus_test.go @@ -0,0 +1,89 @@ +package formatter + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/karitham/thrift-ls/syntax" +) + +// TestFormatRealWorldCorpus feeds every .thrift file under the repo's +// tests/ directories through the format -> reparse -> reformat pipeline. +// These are real-world IDLs (Evernote, Galaxy, line-protocol, lint +// fixtures): shapes the hand-written format cases do not cover. +// +// Invariants per file: +// - the file parses with no errors (they are all valid IDLs) +// - its formatted output parses with no errors +// - formatting is idempotent: format(format(x)) == format(x) +func TestFormatRealWorldCorpus(t *testing.T) { + roots := []string{ + filepath.Join("..", "tests", "made-in-abyss"), + filepath.Join("..", "tests", "evernote-thrift"), + filepath.Join("..", "tests", "galaxy-thrift-api"), + filepath.Join("..", "tests", "line-protocol"), + } + + files := make([]string, 0, 64) + for _, root := range roots { + err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + + if !d.IsDir() && strings.HasSuffix(path, ".thrift") { + files = append(files, path) + } + + return nil + }) + if err != nil { + t.Fatalf("walk %s: %v", root, err) + } + } + + if len(files) == 0 { + t.Fatal("no corpus files found; tests/ missing?") + } + + for _, path := range files { + t.Run(filepath.ToSlash(strings.TrimPrefix(path, ".."+string(filepath.Separator))), func(t *testing.T) { + src, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read: %v", err) + } + + doc := parseBytes(t, src) + + formatted, ferr := Format(doc, DefaultOptions()) + if ferr != nil { + t.Fatalf("format: %v", ferr) + } + + reparsed := parseBytes(t, []byte(formatted)) + + again, ferr := Format(reparsed, DefaultOptions()) + if ferr != nil { + t.Fatalf("reformat: %v", ferr) + } + + if again != formatted { + t.Errorf("not idempotent\nfirst:\n%s\nsecond:\n%s", formatted, again) + } + }) + } +} + +// parseBytes parses src and fails the test on any hard parse error. +func parseBytes(t *testing.T, src []byte) *syntax.Document { + t.Helper() + + doc, errs := syntax.Parse(src) + if hasParseErrors(errs) { + t.Fatalf("unexpected parse errors: %v", errs) + } + + return doc +} -- 2.51.2