| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package modfile |
| |
| import ( |
| "bytes" |
| "io/ioutil" |
| "path/filepath" |
| "strings" |
| "testing" |
| ) |
| |
| // TODO(#45713): Update these tests once AddUse sets the module path. |
| var workAddUseTests = []struct { |
| desc string |
| in string |
| path string |
| modulePath string |
| out string |
| }{ |
| { |
| `empty`, |
| ``, |
| `foo`, `bar`, |
| `use foo`, |
| }, |
| { |
| `go_stmt_only`, |
| `go 1.17 |
| `, |
| `foo`, `bar`, |
| `go 1.17 |
| use foo |
| `, |
| }, |
| { |
| `use_line_present`, |
| `go 1.17 |
| use baz`, |
| `foo`, `bar`, |
| `go 1.17 |
| use ( |
| baz |
| foo |
| ) |
| `, |
| }, |
| { |
| `use_block_present`, |
| `go 1.17 |
| use ( |
| baz |
| quux |
| ) |
| `, |
| `foo`, `bar`, |
| `go 1.17 |
| use ( |
| baz |
| quux |
| foo |
| ) |
| `, |
| }, |
| { |
| `use_and_replace_present`, |
| `go 1.17 |
| use baz |
| replace a => ./b |
| `, |
| `foo`, `bar`, |
| `go 1.17 |
| use ( |
| baz |
| foo |
| ) |
| replace a => ./b |
| `, |
| }, |
| } |
| |
| var workDropUseTests = []struct { |
| desc string |
| in string |
| path string |
| out string |
| }{ |
| { |
| `empty`, |
| ``, |
| `foo`, |
| ``, |
| }, |
| { |
| `go_stmt_only`, |
| `go 1.17 |
| `, |
| `foo`, |
| `go 1.17 |
| `, |
| }, |
| { |
| `single_use`, |
| `go 1.17 |
| use foo`, |
| `foo`, |
| `go 1.17 |
| `, |
| }, |
| { |
| `use_block`, |
| `go 1.17 |
| use ( |
| foo |
| bar |
| baz |
| )`, |
| `bar`, |
| `go 1.17 |
| use ( |
| foo |
| baz |
| )`, |
| }, |
| { |
| `use_multi`, |
| `go 1.17 |
| use ( |
| foo |
| bar |
| baz |
| ) |
| use foo |
| use quux |
| use foo`, |
| `foo`, |
| `go 1.17 |
| use ( |
| bar |
| baz |
| ) |
| use quux`, |
| }, |
| } |
| |
| var workAddGoTests = []struct { |
| desc string |
| in string |
| version string |
| out string |
| }{ |
| { |
| `empty`, |
| ``, |
| `1.17`, |
| `go 1.17 |
| `, |
| }, |
| { |
| `comment`, |
| `// this is a comment`, |
| `1.17`, |
| `// this is a comment |
| |
| go 1.17`, |
| }, |
| { |
| `use_after_replace`, |
| ` |
| replace example.com/foo => ../bar |
| use foo |
| `, |
| `1.17`, |
| ` |
| go 1.17 |
| replace example.com/foo => ../bar |
| use foo |
| `, |
| }, |
| { |
| `use_before_replace`, |
| `use foo |
| replace example.com/foo => ../bar |
| `, |
| `1.17`, |
| ` |
| go 1.17 |
| use foo |
| replace example.com/foo => ../bar |
| `, |
| }, |
| { |
| `use_only`, |
| `use foo |
| `, |
| `1.17`, |
| ` |
| go 1.17 |
| use foo |
| `, |
| }, |
| { |
| `already_have_go`, |
| `go 1.17 |
| `, |
| `1.18`, |
| ` |
| go 1.18 |
| `, |
| }, |
| } |
| |
| var workSortBlocksTests = []struct { |
| desc, in, out string |
| }{ |
| { |
| `use_duplicates_not_removed`, |
| `go 1.17 |
| use foo |
| use bar |
| use ( |
| foo |
| )`, |
| `go 1.17 |
| use foo |
| use bar |
| use ( |
| foo |
| )`, |
| }, |
| { |
| `replace_duplicates_removed`, |
| `go 1.17 |
| use foo |
| replace x.y/z v1.0.0 => ./a |
| replace x.y/z v1.1.0 => ./b |
| replace ( |
| x.y/z v1.0.0 => ./c |
| ) |
| `, |
| `go 1.17 |
| use foo |
| replace x.y/z v1.1.0 => ./b |
| replace ( |
| x.y/z v1.0.0 => ./c |
| ) |
| `, |
| }, |
| } |
| |
| func TestAddUse(t *testing.T) { |
| for _, tt := range workAddUseTests { |
| t.Run(tt.desc, func(t *testing.T) { |
| testWorkEdit(t, tt.in, tt.out, func(f *WorkFile) error { |
| return f.AddUse(tt.path, tt.modulePath) |
| }) |
| }) |
| } |
| } |
| |
| func TestDropUse(t *testing.T) { |
| for _, tt := range workDropUseTests { |
| t.Run(tt.desc, func(t *testing.T) { |
| testWorkEdit(t, tt.in, tt.out, func(f *WorkFile) error { |
| if err := f.DropUse(tt.path); err != nil { |
| return err |
| } |
| f.Cleanup() |
| return nil |
| }) |
| }) |
| } |
| } |
| |
| func TestWorkAddGo(t *testing.T) { |
| for _, tt := range workAddGoTests { |
| t.Run(tt.desc, func(t *testing.T) { |
| testWorkEdit(t, tt.in, tt.out, func(f *WorkFile) error { |
| return f.AddGoStmt(tt.version) |
| }) |
| }) |
| } |
| } |
| |
| func TestWorkSortBlocks(t *testing.T) { |
| for _, tt := range workSortBlocksTests { |
| t.Run(tt.desc, func(t *testing.T) { |
| testWorkEdit(t, tt.in, tt.out, func(f *WorkFile) error { |
| f.SortBlocks() |
| return nil |
| }) |
| }) |
| } |
| } |
| |
| // Test that when files in the testdata directory are parsed |
| // and printed and parsed again, we get the same parse tree |
| // both times. |
| func TestWorkPrintParse(t *testing.T) { |
| outs, err := filepath.Glob("testdata/work/*") |
| if err != nil { |
| t.Fatal(err) |
| } |
| for _, out := range outs { |
| out := out |
| name := filepath.Base(out) |
| t.Run(name, func(t *testing.T) { |
| t.Parallel() |
| data, err := ioutil.ReadFile(out) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| base := "testdata/work/" + filepath.Base(out) |
| f, err := parse(base, data) |
| if err != nil { |
| t.Fatalf("parsing original: %v", err) |
| } |
| |
| ndata := Format(f) |
| f2, err := parse(base, ndata) |
| if err != nil { |
| t.Fatalf("parsing reformatted: %v", err) |
| } |
| |
| eq := eqchecker{file: base} |
| if err := eq.check(f, f2); err != nil { |
| t.Errorf("not equal (parse/Format/parse): %v", err) |
| } |
| |
| pf1, err := ParseWork(base, data, nil) |
| if err != nil { |
| switch base { |
| case "testdata/replace2.in", "testdata/gopkg.in.golden": |
| t.Errorf("should parse %v: %v", base, err) |
| } |
| } |
| if err == nil { |
| pf2, err := ParseWork(base, ndata, nil) |
| if err != nil { |
| t.Fatalf("Parsing reformatted: %v", err) |
| } |
| eq := eqchecker{file: base} |
| if err := eq.check(pf1, pf2); err != nil { |
| t.Errorf("not equal (parse/Format/Parse): %v", err) |
| } |
| |
| ndata2 := Format(pf1.Syntax) |
| pf3, err := ParseWork(base, ndata2, nil) |
| if err != nil { |
| t.Fatalf("Parsing reformatted2: %v", err) |
| } |
| eq = eqchecker{file: base} |
| if err := eq.check(pf1, pf3); err != nil { |
| t.Errorf("not equal (Parse/Format/Parse): %v", err) |
| } |
| ndata = ndata2 |
| } |
| |
| if strings.HasSuffix(out, ".in") { |
| golden, err := ioutil.ReadFile(strings.TrimSuffix(out, ".in") + ".golden") |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(ndata, golden) { |
| t.Errorf("formatted %s incorrectly: diff shows -golden, +ours", base) |
| tdiff(t, string(golden), string(ndata)) |
| return |
| } |
| } |
| }) |
| } |
| } |
| |
| func testWorkEdit(t *testing.T, in, want string, transform func(f *WorkFile) error) *WorkFile { |
| t.Helper() |
| parse := ParseWork |
| f, err := parse("in", []byte(in), nil) |
| if err != nil { |
| t.Fatal(err) |
| } |
| g, err := parse("out", []byte(want), nil) |
| if err != nil { |
| t.Fatal(err) |
| } |
| golden := Format(g.Syntax) |
| |
| if err := transform(f); err != nil { |
| t.Fatal(err) |
| } |
| out := Format(f.Syntax) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(out, golden) { |
| t.Errorf("have:\n%s\nwant:\n%s", out, golden) |
| } |
| |
| return f |
| } |